diff --git a/physical/azure.go b/physical/azure.go
index 94a0024bf..3bb682764 100644
--- a/physical/azure.go
+++ b/physical/azure.go
@@ -10,11 +10,12 @@ import (
"strings"
"time"
+ storage "github.com/Azure/azure-sdk-for-go/storage"
log "github.com/mgutz/logxi/v1"
- "github.com/Azure/azure-storage-go"
"github.com/armon/go-metrics"
"github.com/hashicorp/errwrap"
+ cleanhttp "github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/vault/helper/strutil"
)
@@ -24,8 +25,7 @@ var MaxBlobSize = 1024 * 1024 * 4
// AzureBackend is a physical backend that stores data
// within an Azure blob container.
type AzureBackend struct {
- container string
- client storage.BlobStorageClient
+ container *storage.Container
logger log.Logger
permitPool *PermitPool
}
@@ -34,11 +34,10 @@ type AzureBackend struct {
// bucket. Credentials can be provided to the backend, sourced
// from the environment, AWS credential files or by IAM role.
func newAzureBackend(conf map[string]string, logger log.Logger) (Backend, error) {
-
- container := os.Getenv("AZURE_BLOB_CONTAINER")
- if container == "" {
- container = conf["container"]
- if container == "" {
+ name := os.Getenv("AZURE_BLOB_CONTAINER")
+ if name == "" {
+ name = conf["container"]
+ if name == "" {
return nil, fmt.Errorf("'container' must be set")
}
}
@@ -63,19 +62,15 @@ func newAzureBackend(conf map[string]string, logger log.Logger) (Backend, error)
if err != nil {
return nil, fmt.Errorf("failed to create Azure client: %v", err)
}
+ client.HTTPClient = cleanhttp.DefaultPooledClient()
- contObj := client.GetBlobService().GetContainerReference(container)
- created, err := contObj.CreateIfNotExists()
+ blobClient := client.GetBlobService()
+ container := blobClient.GetContainerReference(name)
+ _, err = container.CreateIfNotExists(&storage.CreateContainerOptions{
+ Access: storage.ContainerAccessTypePrivate,
+ })
if err != nil {
- return nil, fmt.Errorf("failed to upsert container: %v", err)
- }
- if created {
- err = contObj.SetPermissions(storage.ContainerPermissions{
- AccessType: storage.ContainerAccessTypePrivate,
- }, 0, "")
- if err != nil {
- return nil, fmt.Errorf("failed to set permissions on newly-created container: %v", err)
- }
+ return nil, fmt.Errorf("failed to create %q container: %v", name, err)
}
maxParStr, ok := conf["max_parallel"]
@@ -92,7 +87,6 @@ func newAzureBackend(conf map[string]string, logger log.Logger) (Backend, error)
a := &AzureBackend{
container: container,
- client: client.GetBlobService(),
logger: logger,
permitPool: NewPermitPool(maxParInt),
}
@@ -104,7 +98,7 @@ func (a *AzureBackend) Put(entry *Entry) error {
defer metrics.MeasureSince([]string{"azure", "put"}, time.Now())
if len(entry.Value) >= MaxBlobSize {
- return fmt.Errorf("Value is bigger than the current supported limit of 4MBytes")
+ return fmt.Errorf("value is bigger than the current supported limit of 4MBytes")
}
blockID := base64.StdEncoding.EncodeToString([]byte("AAAA"))
@@ -114,10 +108,15 @@ func (a *AzureBackend) Put(entry *Entry) error {
a.permitPool.Acquire()
defer a.permitPool.Release()
- err := a.client.PutBlock(a.container, entry.Key, blockID, entry.Value)
+ blob := &storage.Blob{
+ Container: a.container,
+ Name: entry.Key,
+ }
+ if err := blob.PutBlock(blockID, entry.Value, nil); err != nil {
+ return err
+ }
- err = a.client.PutBlockList(a.container, entry.Key, blocks)
- return err
+ return blob.PutBlockList(blocks, nil)
}
// Get is used to fetch an entry
@@ -127,18 +126,23 @@ func (a *AzureBackend) Get(key string) (*Entry, error) {
a.permitPool.Acquire()
defer a.permitPool.Release()
- exists, _ := a.client.BlobExists(a.container, key)
-
+ blob := &storage.Blob{
+ Container: a.container,
+ Name: key,
+ }
+ exists, err := blob.Exists()
+ if err != nil {
+ return nil, err
+ }
if !exists {
return nil, nil
}
- reader, err := a.client.GetBlob(a.container, key)
-
+ reader, err := blob.Get(nil)
if err != nil {
return nil, err
}
-
+ defer reader.Close()
data, err := ioutil.ReadAll(reader)
ent := &Entry{
@@ -153,10 +157,15 @@ func (a *AzureBackend) Get(key string) (*Entry, error) {
func (a *AzureBackend) Delete(key string) error {
defer metrics.MeasureSince([]string{"azure", "delete"}, time.Now())
+ blob := &storage.Blob{
+ Container: a.container,
+ Name: key,
+ }
+
a.permitPool.Acquire()
defer a.permitPool.Release()
- _, err := a.client.DeleteBlobIfExists(a.container, key, nil)
+ _, err := blob.DeleteIfExists(nil)
return err
}
@@ -166,15 +175,13 @@ func (a *AzureBackend) List(prefix string) ([]string, error) {
defer metrics.MeasureSince([]string{"azure", "list"}, time.Now())
a.permitPool.Acquire()
- defer a.permitPool.Release()
-
- contObj := a.client.GetContainerReference(a.container)
- list, err := contObj.ListBlobs(storage.ListBlobsParameters{Prefix: prefix})
-
+ list, err := a.container.ListBlobs(storage.ListBlobsParameters{Prefix: prefix})
if err != nil {
// Break early.
+ a.permitPool.Release()
return nil, err
}
+ a.permitPool.Release()
keys := []string{}
for _, blob := range list.Blobs {
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/LICENSE b/vendor/github.com/Azure/azure-sdk-for-go/LICENSE
new file mode 100644
index 000000000..af39a91e7
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2016 Microsoft Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/appendblob.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/appendblob.go
new file mode 100644
index 000000000..3292cb556
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/appendblob.go
@@ -0,0 +1,70 @@
+package storage
+
+import (
+ "bytes"
+ "fmt"
+ "net/http"
+ "net/url"
+ "time"
+)
+
+// PutAppendBlob initializes an empty append blob with specified name. An
+// append blob must be created using this method before appending blocks.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Blob
+func (b *Blob) PutAppendBlob(options *PutBlobOptions) error {
+ params := url.Values{}
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers["x-ms-blob-type"] = string(BlobTypeAppend)
+ headers = mergeHeaders(headers, headersFromStruct(b.Properties))
+ headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusCreated})
+}
+
+// AppendBlockOptions includes the options for an append block operation
+type AppendBlockOptions struct {
+ Timeout uint
+ LeaseID string `header:"x-ms-lease-id"`
+ MaxSize *uint `header:"x-ms-blob-condition-maxsize"`
+ AppendPosition *uint `header:"x-ms-blob-condition-appendpos"`
+ IfModifiedSince *time.Time `header:"If-Modified-Since"`
+ IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
+ IfMatch string `header:"If-Match"`
+ IfNoneMatch string `header:"If-None-Match"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// AppendBlock appends a block to an append blob.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Append-Block
+func (b *Blob) AppendBlock(chunk []byte, options *AppendBlockOptions) error {
+ params := url.Values{"comp": {"appendblock"}}
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers["x-ms-blob-type"] = string(BlobTypeAppend)
+ headers["Content-Length"] = fmt.Sprintf("%v", len(chunk))
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, bytes.NewReader(chunk), b.Container.bsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusCreated})
+}
diff --git a/vendor/github.com/Azure/azure-storage-go/authorization.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/authorization.go
similarity index 85%
rename from vendor/github.com/Azure/azure-storage-go/authorization.go
rename to vendor/github.com/Azure/azure-sdk-for-go/storage/authorization.go
index 89a0d0b3c..608bf3133 100644
--- a/vendor/github.com/Azure/azure-storage-go/authorization.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/authorization.go
@@ -20,20 +20,24 @@ const (
sharedKeyLiteForTable authentication = "sharedKeyLiteTable"
// headers
- headerAuthorization = "Authorization"
- headerContentLength = "Content-Length"
- headerDate = "Date"
- headerXmsDate = "x-ms-date"
- headerXmsVersion = "x-ms-version"
- headerContentEncoding = "Content-Encoding"
- headerContentLanguage = "Content-Language"
- headerContentType = "Content-Type"
- headerContentMD5 = "Content-MD5"
- headerIfModifiedSince = "If-Modified-Since"
- headerIfMatch = "If-Match"
- headerIfNoneMatch = "If-None-Match"
- headerIfUnmodifiedSince = "If-Unmodified-Since"
- headerRange = "Range"
+ headerAcceptCharset = "Accept-Charset"
+ headerAuthorization = "Authorization"
+ headerContentLength = "Content-Length"
+ headerDate = "Date"
+ headerXmsDate = "x-ms-date"
+ headerXmsVersion = "x-ms-version"
+ headerContentEncoding = "Content-Encoding"
+ headerContentLanguage = "Content-Language"
+ headerContentType = "Content-Type"
+ headerContentMD5 = "Content-MD5"
+ headerIfModifiedSince = "If-Modified-Since"
+ headerIfMatch = "If-Match"
+ headerIfNoneMatch = "If-None-Match"
+ headerIfUnmodifiedSince = "If-Unmodified-Since"
+ headerRange = "Range"
+ headerDataServiceVersion = "DataServiceVersion"
+ headerMaxDataServiceVersion = "MaxDataServiceVersion"
+ headerContentTransferEncoding = "Content-Transfer-Encoding"
)
func (c *Client) addAuthorizationHeader(verb, url string, headers map[string]string, auth authentication) (map[string]string, error) {
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/blob.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/blob.go
new file mode 100644
index 000000000..dd9eb386c
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/blob.go
@@ -0,0 +1,617 @@
+package storage
+
+import (
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// A Blob is an entry in BlobListResponse.
+type Blob struct {
+ Container *Container
+ Name string `xml:"Name"`
+ Snapshot time.Time `xml:"Snapshot"`
+ Properties BlobProperties `xml:"Properties"`
+ Metadata BlobMetadata `xml:"Metadata"`
+}
+
+// PutBlobOptions includes the options any put blob operation
+// (page, block, append)
+type PutBlobOptions struct {
+ Timeout uint
+ LeaseID string `header:"x-ms-lease-id"`
+ Origin string `header:"Origin"`
+ IfModifiedSince *time.Time `header:"If-Modified-Since"`
+ IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
+ IfMatch string `header:"If-Match"`
+ IfNoneMatch string `header:"If-None-Match"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// BlobMetadata is a set of custom name/value pairs.
+//
+// See https://msdn.microsoft.com/en-us/library/azure/dd179404.aspx
+type BlobMetadata map[string]string
+
+type blobMetadataEntries struct {
+ Entries []blobMetadataEntry `xml:",any"`
+}
+type blobMetadataEntry struct {
+ XMLName xml.Name
+ Value string `xml:",chardata"`
+}
+
+// UnmarshalXML converts the xml:Metadata into Metadata map
+func (bm *BlobMetadata) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ var entries blobMetadataEntries
+ if err := d.DecodeElement(&entries, &start); err != nil {
+ return err
+ }
+ for _, entry := range entries.Entries {
+ if *bm == nil {
+ *bm = make(BlobMetadata)
+ }
+ (*bm)[strings.ToLower(entry.XMLName.Local)] = entry.Value
+ }
+ return nil
+}
+
+// MarshalXML implements the xml.Marshaler interface. It encodes
+// metadata name/value pairs as they would appear in an Azure
+// ListBlobs response.
+func (bm BlobMetadata) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {
+ entries := make([]blobMetadataEntry, 0, len(bm))
+ for k, v := range bm {
+ entries = append(entries, blobMetadataEntry{
+ XMLName: xml.Name{Local: http.CanonicalHeaderKey(k)},
+ Value: v,
+ })
+ }
+ return enc.EncodeElement(blobMetadataEntries{
+ Entries: entries,
+ }, start)
+}
+
+// BlobProperties contains various properties of a blob
+// returned in various endpoints like ListBlobs or GetBlobProperties.
+type BlobProperties struct {
+ LastModified TimeRFC1123 `xml:"Last-Modified"`
+ Etag string `xml:"Etag"`
+ ContentMD5 string `xml:"Content-MD5" header:"x-ms-blob-content-md5"`
+ ContentLength int64 `xml:"Content-Length"`
+ ContentType string `xml:"Content-Type" header:"x-ms-blob-content-type"`
+ ContentEncoding string `xml:"Content-Encoding" header:"x-ms-blob-content-encoding"`
+ CacheControl string `xml:"Cache-Control" header:"x-ms-blob-cache-control"`
+ ContentLanguage string `xml:"Cache-Language" header:"x-ms-blob-content-language"`
+ ContentDisposition string `xml:"Content-Disposition" header:"x-ms-blob-content-disposition"`
+ BlobType BlobType `xml:"x-ms-blob-blob-type"`
+ SequenceNumber int64 `xml:"x-ms-blob-sequence-number"`
+ CopyID string `xml:"CopyId"`
+ CopyStatus string `xml:"CopyStatus"`
+ CopySource string `xml:"CopySource"`
+ CopyProgress string `xml:"CopyProgress"`
+ CopyCompletionTime TimeRFC1123 `xml:"CopyCompletionTime"`
+ CopyStatusDescription string `xml:"CopyStatusDescription"`
+ LeaseStatus string `xml:"LeaseStatus"`
+ LeaseState string `xml:"LeaseState"`
+ LeaseDuration string `xml:"LeaseDuration"`
+ ServerEncrypted bool `xml:"ServerEncrypted"`
+ IncrementalCopy bool `xml:"IncrementalCopy"`
+}
+
+// BlobType defines the type of the Azure Blob.
+type BlobType string
+
+// Types of page blobs
+const (
+ BlobTypeBlock BlobType = "BlockBlob"
+ BlobTypePage BlobType = "PageBlob"
+ BlobTypeAppend BlobType = "AppendBlob"
+)
+
+func (b *Blob) buildPath() string {
+ return b.Container.buildPath() + "/" + b.Name
+}
+
+// Exists returns true if a blob with given name exists on the specified
+// container of the storage account.
+func (b *Blob) Exists() (bool, error) {
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), nil)
+ headers := b.Container.bsc.client.getStandardHeaders()
+ resp, err := b.Container.bsc.client.exec(http.MethodHead, uri, headers, nil, b.Container.bsc.auth)
+ if resp != nil {
+ defer readAndCloseBody(resp.body)
+ if resp.statusCode == http.StatusOK || resp.statusCode == http.StatusNotFound {
+ return resp.statusCode == http.StatusOK, nil
+ }
+ }
+ return false, err
+}
+
+// GetURL gets the canonical URL to the blob with the specified name in the
+// specified container. If name is not specified, the canonical URL for the entire
+// container is obtained.
+// This method does not create a publicly accessible URL if the blob or container
+// is private and this method does not check if the blob exists.
+func (b *Blob) GetURL() string {
+ container := b.Container.Name
+ if container == "" {
+ container = "$root"
+ }
+ return b.Container.bsc.client.getEndpoint(blobServiceName, pathForResource(container, b.Name), nil)
+}
+
+// GetBlobRangeOptions includes the options for a get blob range operation
+type GetBlobRangeOptions struct {
+ Range *BlobRange
+ GetRangeContentMD5 bool
+ *GetBlobOptions
+}
+
+// GetBlobOptions includes the options for a get blob operation
+type GetBlobOptions struct {
+ Timeout uint
+ Snapshot *time.Time
+ LeaseID string `header:"x-ms-lease-id"`
+ Origin string `header:"Origin"`
+ IfModifiedSince *time.Time `header:"If-Modified-Since"`
+ IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
+ IfMatch string `header:"If-Match"`
+ IfNoneMatch string `header:"If-None-Match"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// BlobRange represents the bytes range to be get
+type BlobRange struct {
+ Start uint64
+ End uint64
+}
+
+func (br BlobRange) String() string {
+ return fmt.Sprintf("bytes=%d-%d", br.Start, br.End)
+}
+
+// Get returns a stream to read the blob. Caller must call both Read and Close()
+// to correctly close the underlying connection.
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Get-Blob
+func (b *Blob) Get(options *GetBlobOptions) (io.ReadCloser, error) {
+ rangeOptions := GetBlobRangeOptions{
+ GetBlobOptions: options,
+ }
+ resp, err := b.getRange(&rangeOptions)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
+ return nil, err
+ }
+ if err := b.writePropoerties(resp.headers); err != nil {
+ return resp.body, err
+ }
+ return resp.body, nil
+}
+
+// GetRange reads the specified range of a blob to a stream. The bytesRange
+// string must be in a format like "0-", "10-100" as defined in HTTP 1.1 spec.
+// Caller must call both Read and Close()// to correctly close the underlying
+// connection.
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Get-Blob
+func (b *Blob) GetRange(options *GetBlobRangeOptions) (io.ReadCloser, error) {
+ resp, err := b.getRange(options)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := checkRespCode(resp.statusCode, []int{http.StatusPartialContent}); err != nil {
+ return nil, err
+ }
+ if err := b.writePropoerties(resp.headers); err != nil {
+ return resp.body, err
+ }
+ return resp.body, nil
+}
+
+func (b *Blob) getRange(options *GetBlobRangeOptions) (*storageResponse, error) {
+ params := url.Values{}
+ headers := b.Container.bsc.client.getStandardHeaders()
+
+ if options != nil {
+ if options.Range != nil {
+ headers["Range"] = options.Range.String()
+ headers["x-ms-range-get-content-md5"] = fmt.Sprintf("%v", options.GetRangeContentMD5)
+ }
+ if options.GetBlobOptions != nil {
+ headers = mergeHeaders(headers, headersFromStruct(*options.GetBlobOptions))
+ params = addTimeout(params, options.Timeout)
+ params = addSnapshot(params, options.Snapshot)
+ }
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodGet, uri, headers, nil, b.Container.bsc.auth)
+ if err != nil {
+ return nil, err
+ }
+ return resp, err
+}
+
+// SnapshotOptions includes the options for a snapshot blob operation
+type SnapshotOptions struct {
+ Timeout uint
+ LeaseID string `header:"x-ms-lease-id"`
+ IfModifiedSince *time.Time `header:"If-Modified-Since"`
+ IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
+ IfMatch string `header:"If-Match"`
+ IfNoneMatch string `header:"If-None-Match"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// CreateSnapshot creates a snapshot for a blob
+// See https://msdn.microsoft.com/en-us/library/azure/ee691971.aspx
+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)
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
+ if err != nil || resp == nil {
+ return nil, err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err := checkRespCode(resp.statusCode, []int{http.StatusCreated}); err != nil {
+ return nil, err
+ }
+
+ snapshotResponse := resp.headers.Get(http.CanonicalHeaderKey("x-ms-snapshot"))
+ if snapshotResponse != "" {
+ snapshotTimestamp, err := time.Parse(time.RFC3339, snapshotResponse)
+ if err != nil {
+ return nil, err
+ }
+ return &snapshotTimestamp, nil
+ }
+
+ return nil, errors.New("Snapshot not created")
+}
+
+// GetBlobPropertiesOptions includes the options for a get blob properties operation
+type GetBlobPropertiesOptions struct {
+ Timeout uint
+ Snapshot *time.Time
+ LeaseID string `header:"x-ms-lease-id"`
+ IfModifiedSince *time.Time `header:"If-Modified-Since"`
+ IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
+ IfMatch string `header:"If-Match"`
+ IfNoneMatch string `header:"If-None-Match"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// GetProperties provides various information about the specified blob.
+// See https://msdn.microsoft.com/en-us/library/azure/dd179394.aspx
+func (b *Blob) GetProperties(options *GetBlobPropertiesOptions) error {
+ params := url.Values{}
+ headers := b.Container.bsc.client.getStandardHeaders()
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ params = addSnapshot(params, options.Snapshot)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodHead, uri, headers, nil, b.Container.bsc.auth)
+ if err != nil {
+ return err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
+ return err
+ }
+ return b.writePropoerties(resp.headers)
+}
+
+func (b *Blob) writePropoerties(h http.Header) error {
+ var err error
+
+ var contentLength int64
+ contentLengthStr := h.Get("Content-Length")
+ if contentLengthStr != "" {
+ contentLength, err = strconv.ParseInt(contentLengthStr, 0, 64)
+ if err != nil {
+ return err
+ }
+ }
+
+ var sequenceNum int64
+ sequenceNumStr := h.Get("x-ms-blob-sequence-number")
+ if sequenceNumStr != "" {
+ sequenceNum, err = strconv.ParseInt(sequenceNumStr, 0, 64)
+ if err != nil {
+ return err
+ }
+ }
+
+ lastModified, err := getTimeFromHeaders(h, "Last-Modified")
+ if err != nil {
+ return err
+ }
+
+ copyCompletionTime, err := getTimeFromHeaders(h, "x-ms-copy-completion-time")
+ if err != nil {
+ return err
+ }
+
+ b.Properties = BlobProperties{
+ LastModified: TimeRFC1123(*lastModified),
+ Etag: h.Get("Etag"),
+ ContentMD5: h.Get("Content-MD5"),
+ ContentLength: contentLength,
+ ContentEncoding: h.Get("Content-Encoding"),
+ ContentType: h.Get("Content-Type"),
+ ContentDisposition: h.Get("Content-Disposition"),
+ CacheControl: h.Get("Cache-Control"),
+ ContentLanguage: h.Get("Content-Language"),
+ SequenceNumber: sequenceNum,
+ CopyCompletionTime: TimeRFC1123(*copyCompletionTime),
+ CopyStatusDescription: h.Get("x-ms-copy-status-description"),
+ CopyID: h.Get("x-ms-copy-id"),
+ CopyProgress: h.Get("x-ms-copy-progress"),
+ CopySource: h.Get("x-ms-copy-source"),
+ CopyStatus: h.Get("x-ms-copy-status"),
+ BlobType: BlobType(h.Get("x-ms-blob-type")),
+ LeaseStatus: h.Get("x-ms-lease-status"),
+ LeaseState: h.Get("x-ms-lease-state"),
+ }
+ b.writeMetadata(h)
+ return nil
+}
+
+// SetBlobPropertiesOptions contains various properties of a blob and is an entry
+// in SetProperties
+type SetBlobPropertiesOptions struct {
+ Timeout uint
+ LeaseID string `header:"x-ms-lease-id"`
+ Origin string `header:"Origin"`
+ IfModifiedSince *time.Time `header:"If-Modified-Since"`
+ IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
+ IfMatch string `header:"If-Match"`
+ IfNoneMatch string `header:"If-None-Match"`
+ SequenceNumberAction *SequenceNumberAction
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// SequenceNumberAction defines how the blob's sequence number should be modified
+type SequenceNumberAction string
+
+// Options for sequence number action
+const (
+ SequenceNumberActionMax SequenceNumberAction = "max"
+ SequenceNumberActionUpdate SequenceNumberAction = "update"
+ SequenceNumberActionIncrement SequenceNumberAction = "increment"
+)
+
+// SetProperties replaces the BlobHeaders for the specified blob.
+//
+// Some keys may be converted to Camel-Case before sending. All keys
+// are returned in lower case by GetBlobProperties. HTTP header names
+// are case-insensitive so case munging should not matter to other
+// applications either.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Set-Blob-Properties
+func (b *Blob) SetProperties(options *SetBlobPropertiesOptions) error {
+ params := url.Values{"comp": {"properties"}}
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers = mergeHeaders(headers, headersFromStruct(b.Properties))
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ if b.Properties.BlobType == BlobTypePage {
+ headers = addToHeaders(headers, "x-ms-blob-content-length", fmt.Sprintf("byte %v", b.Properties.ContentLength))
+ if options != nil || options.SequenceNumberAction != nil {
+ headers = addToHeaders(headers, "x-ms-sequence-number-action", string(*options.SequenceNumberAction))
+ if *options.SequenceNumberAction != SequenceNumberActionIncrement {
+ headers = addToHeaders(headers, "x-ms-blob-sequence-number", fmt.Sprintf("%v", b.Properties.SequenceNumber))
+ }
+ }
+ }
+
+ resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusOK})
+}
+
+// SetBlobMetadataOptions includes the options for a set blob metadata operation
+type SetBlobMetadataOptions struct {
+ Timeout uint
+ LeaseID string `header:"x-ms-lease-id"`
+ IfModifiedSince *time.Time `header:"If-Modified-Since"`
+ IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
+ IfMatch string `header:"If-Match"`
+ IfNoneMatch string `header:"If-None-Match"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// SetMetadata replaces the metadata for the specified blob.
+//
+// Some keys may be converted to Camel-Case before sending. All keys
+// are returned in lower case by GetBlobMetadata. HTTP header names
+// are case-insensitive so case munging should not matter to other
+// applications either.
+//
+// See https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx
+func (b *Blob) SetMetadata(options *SetBlobMetadataOptions) error {
+ params := url.Values{"comp": {"metadata"}}
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusOK})
+}
+
+// GetBlobMetadataOptions includes the options for a get blob metadata operation
+type GetBlobMetadataOptions struct {
+ Timeout uint
+ Snapshot *time.Time
+ LeaseID string `header:"x-ms-lease-id"`
+ IfModifiedSince *time.Time `header:"If-Modified-Since"`
+ IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
+ IfMatch string `header:"If-Match"`
+ IfNoneMatch string `header:"If-None-Match"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// GetMetadata returns all user-defined metadata for the specified blob.
+//
+// All metadata keys will be returned in lower case. (HTTP header
+// names are case-insensitive.)
+//
+// See https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx
+func (b *Blob) GetMetadata(options *GetBlobMetadataOptions) error {
+ params := url.Values{"comp": {"metadata"}}
+ headers := b.Container.bsc.client.getStandardHeaders()
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ params = addSnapshot(params, options.Snapshot)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodGet, uri, headers, nil, b.Container.bsc.auth)
+ if err != nil {
+ return err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
+ return err
+ }
+
+ b.writeMetadata(resp.headers)
+ return nil
+}
+
+func (b *Blob) writeMetadata(h http.Header) {
+ metadata := make(map[string]string)
+ for k, v := range h {
+ // Can't trust CanonicalHeaderKey() to munge case
+ // reliably. "_" is allowed in identifiers:
+ // https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx
+ // https://msdn.microsoft.com/library/aa664670(VS.71).aspx
+ // http://tools.ietf.org/html/rfc7230#section-3.2
+ // ...but "_" is considered invalid by
+ // CanonicalMIMEHeaderKey in
+ // https://golang.org/src/net/textproto/reader.go?s=14615:14659#L542
+ // so k can be "X-Ms-Meta-Lol" or "x-ms-meta-lol_rofl".
+ k = strings.ToLower(k)
+ if len(v) == 0 || !strings.HasPrefix(k, strings.ToLower(userDefinedMetadataHeaderPrefix)) {
+ continue
+ }
+ // metadata["lol"] = content of the last X-Ms-Meta-Lol header
+ k = k[len(userDefinedMetadataHeaderPrefix):]
+ metadata[k] = v[len(v)-1]
+ }
+
+ b.Metadata = BlobMetadata(metadata)
+}
+
+// DeleteBlobOptions includes the options for a delete blob operation
+type DeleteBlobOptions struct {
+ Timeout uint
+ Snapshot *time.Time
+ LeaseID string `header:"x-ms-lease-id"`
+ DeleteSnapshots *bool
+ IfModifiedSince *time.Time `header:"If-Modified-Since"`
+ IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
+ IfMatch string `header:"If-Match"`
+ IfNoneMatch string `header:"If-None-Match"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// Delete deletes the given blob from the specified container.
+// If the blob does not exists at the time of the Delete Blob operation, it
+// returns error.
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Delete-Blob
+func (b *Blob) Delete(options *DeleteBlobOptions) error {
+ resp, err := b.delete(options)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusAccepted})
+}
+
+// DeleteIfExists deletes the given blob from the specified container If the
+// blob is deleted with this call, returns true. Otherwise returns false.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Delete-Blob
+func (b *Blob) DeleteIfExists(options *DeleteBlobOptions) (bool, error) {
+ resp, err := b.delete(options)
+ if resp != nil {
+ defer readAndCloseBody(resp.body)
+ if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound {
+ return resp.statusCode == http.StatusAccepted, nil
+ }
+ }
+ return false, err
+}
+
+func (b *Blob) delete(options *DeleteBlobOptions) (*storageResponse, error) {
+ params := url.Values{}
+ headers := b.Container.bsc.client.getStandardHeaders()
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ params = addSnapshot(params, options.Snapshot)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ if options.DeleteSnapshots != nil {
+ if *options.DeleteSnapshots {
+ headers["x-ms-delete-snapshots"] = "include"
+ } else {
+ headers["x-ms-delete-snapshots"] = "only"
+ }
+ }
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+ return b.Container.bsc.client.exec(http.MethodDelete, uri, headers, nil, b.Container.bsc.auth)
+}
+
+// helper method to construct the path to either a blob or container
+func pathForResource(container, name string) string {
+ if name != "" {
+ return fmt.Sprintf("/%s/%s", container, name)
+ }
+ return fmt.Sprintf("/%s", container)
+}
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/blobsasuri.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/blobsasuri.go
new file mode 100644
index 000000000..43173d3a4
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/blobsasuri.go
@@ -0,0 +1,106 @@
+package storage
+
+import (
+ "errors"
+ "fmt"
+ "net/url"
+ "strings"
+ "time"
+)
+
+// GetSASURIWithSignedIPAndProtocol creates an URL to the specified blob which contains the Shared
+// Access Signature with specified permissions and expiration time. Also includes signedIPRange and allowed protocols.
+// If old API version is used but no signedIP is passed (ie empty string) then this should still work.
+// We only populate the signedIP when it non-empty.
+//
+// See https://msdn.microsoft.com/en-us/library/azure/ee395415.aspx
+func (b *Blob) GetSASURIWithSignedIPAndProtocol(expiry time.Time, permissions string, signedIPRange string, HTTPSOnly bool) (string, error) {
+ var (
+ signedPermissions = permissions
+ blobURL = b.GetURL()
+ )
+ canonicalizedResource, err := b.Container.bsc.client.buildCanonicalizedResource(blobURL, b.Container.bsc.auth)
+ if err != nil {
+ return "", err
+ }
+
+ // "The canonicalizedresouce portion of the string is a canonical path to the signed resource.
+ // It must include the service name (blob, table, queue or file) for version 2015-02-21 or
+ // later, the storage account name, and the resource name, and must be URL-decoded.
+ // -- https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
+
+ // We need to replace + with %2b first to avoid being treated as a space (which is correct for query strings, but not the path component).
+ canonicalizedResource = strings.Replace(canonicalizedResource, "+", "%2b", -1)
+ canonicalizedResource, err = url.QueryUnescape(canonicalizedResource)
+ if err != nil {
+ return "", err
+ }
+
+ signedExpiry := expiry.UTC().Format(time.RFC3339)
+
+ //If blob name is missing, resource is a container
+ signedResource := "c"
+ if len(b.Name) > 0 {
+ signedResource = "b"
+ }
+
+ protocols := "https,http"
+ if HTTPSOnly {
+ protocols = "https"
+ }
+ stringToSign, err := blobSASStringToSign(b.Container.bsc.client.apiVersion, canonicalizedResource, signedExpiry, signedPermissions, signedIPRange, protocols)
+ if err != nil {
+ return "", err
+ }
+
+ sig := b.Container.bsc.client.computeHmac256(stringToSign)
+ sasParams := url.Values{
+ "sv": {b.Container.bsc.client.apiVersion},
+ "se": {signedExpiry},
+ "sr": {signedResource},
+ "sp": {signedPermissions},
+ "sig": {sig},
+ }
+
+ if b.Container.bsc.client.apiVersion >= "2015-04-05" {
+ sasParams.Add("spr", protocols)
+ if signedIPRange != "" {
+ sasParams.Add("sip", signedIPRange)
+ }
+ }
+
+ sasURL, err := url.Parse(blobURL)
+ if err != nil {
+ return "", err
+ }
+ sasURL.RawQuery = sasParams.Encode()
+ return sasURL.String(), nil
+}
+
+// GetSASURI creates an URL to the specified blob which contains the Shared
+// Access Signature with specified permissions and expiration time.
+//
+// See https://msdn.microsoft.com/en-us/library/azure/ee395415.aspx
+func (b *Blob) GetSASURI(expiry time.Time, permissions string) (string, error) {
+ return b.GetSASURIWithSignedIPAndProtocol(expiry, permissions, "", false)
+}
+
+func blobSASStringToSign(signedVersion, canonicalizedResource, signedExpiry, signedPermissions string, signedIP string, protocols string) (string, error) {
+ var signedStart, signedIdentifier, rscc, rscd, rsce, rscl, rsct string
+
+ if signedVersion >= "2015-02-21" {
+ canonicalizedResource = "/blob" + canonicalizedResource
+ }
+
+ // https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx#Anchor_12
+ if signedVersion >= "2015-04-05" {
+ return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedIP, protocols, signedVersion, rscc, rscd, rsce, rscl, rsct), nil
+ }
+
+ // reference: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
+ if signedVersion >= "2013-08-15" {
+ return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedVersion, rscc, rscd, rsce, rscl, rsct), nil
+ }
+
+ return "", errors.New("storage: not implemented SAS for versions earlier than 2013-08-15")
+}
diff --git a/vendor/github.com/Azure/azure-storage-go/blobserviceclient.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/blobserviceclient.go
similarity index 89%
rename from vendor/github.com/Azure/azure-storage-go/blobserviceclient.go
rename to vendor/github.com/Azure/azure-sdk-for-go/storage/blobserviceclient.go
index e5911ac81..450b20f96 100644
--- a/vendor/github.com/Azure/azure-storage-go/blobserviceclient.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/blobserviceclient.go
@@ -1,9 +1,9 @@
package storage
import (
- "fmt"
"net/http"
"net/url"
+ "strconv"
)
// BlobStorageClient contains operations for Microsoft Azure Blob Storage
@@ -38,9 +38,9 @@ type ListContainersParameters struct {
}
// GetContainerReference returns a Container object for the specified container name.
-func (b BlobStorageClient) GetContainerReference(name string) Container {
- return Container{
- bsc: &b,
+func (b *BlobStorageClient) GetContainerReference(name string) *Container {
+ return &Container{
+ bsc: b,
Name: name,
}
}
@@ -61,6 +61,9 @@ func (b BlobStorageClient) ListContainers(params ListContainersParameters) (*Con
}
defer resp.body.Close()
err = xmlUnmarshal(resp.body, &out)
+ if err != nil {
+ return nil, err
+ }
// assign our client to the newly created Container objects
for i := range out.Containers {
@@ -82,10 +85,10 @@ func (p ListContainersParameters) getParameters() url.Values {
out.Set("include", p.Include)
}
if p.MaxResults != 0 {
- out.Set("maxresults", fmt.Sprintf("%v", p.MaxResults))
+ out.Set("maxresults", strconv.FormatUint(uint64(p.MaxResults), 10))
}
if p.Timeout != 0 {
- out.Set("timeout", fmt.Sprintf("%v", p.Timeout))
+ out.Set("timeout", strconv.FormatUint(uint64(p.Timeout), 10))
}
return out
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/blockblob.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/blockblob.go
new file mode 100644
index 000000000..6a180d48d
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/blockblob.go
@@ -0,0 +1,240 @@
+package storage
+
+import (
+ "bytes"
+ "encoding/xml"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// BlockListType is used to filter out types of blocks in a Get Blocks List call
+// for a block blob.
+//
+// See https://msdn.microsoft.com/en-us/library/azure/dd179400.aspx for all
+// block types.
+type BlockListType string
+
+// Filters for listing blocks in block blobs
+const (
+ BlockListTypeAll BlockListType = "all"
+ BlockListTypeCommitted BlockListType = "committed"
+ BlockListTypeUncommitted BlockListType = "uncommitted"
+)
+
+// Maximum sizes (per REST API) for various concepts
+const (
+ MaxBlobBlockSize = 100 * 1024 * 1024
+ MaxBlobPageSize = 4 * 1024 * 1024
+)
+
+// BlockStatus defines states a block for a block blob can
+// be in.
+type BlockStatus string
+
+// List of statuses that can be used to refer to a block in a block list
+const (
+ BlockStatusUncommitted BlockStatus = "Uncommitted"
+ BlockStatusCommitted BlockStatus = "Committed"
+ BlockStatusLatest BlockStatus = "Latest"
+)
+
+// Block is used to create Block entities for Put Block List
+// call.
+type Block struct {
+ ID string
+ Status BlockStatus
+}
+
+// BlockListResponse contains the response fields from Get Block List call.
+//
+// See https://msdn.microsoft.com/en-us/library/azure/dd179400.aspx
+type BlockListResponse struct {
+ XMLName xml.Name `xml:"BlockList"`
+ CommittedBlocks []BlockResponse `xml:"CommittedBlocks>Block"`
+ UncommittedBlocks []BlockResponse `xml:"UncommittedBlocks>Block"`
+}
+
+// BlockResponse contains the block information returned
+// in the GetBlockListCall.
+type BlockResponse struct {
+ Name string `xml:"Name"`
+ Size int64 `xml:"Size"`
+}
+
+// CreateBlockBlob initializes an empty block blob with no blocks.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Blob
+func (b *Blob) CreateBlockBlob(options *PutBlobOptions) error {
+ return b.CreateBlockBlobFromReader(nil, options)
+}
+
+// CreateBlockBlobFromReader initializes a block blob using data from
+// reader. Size must be the number of bytes read from reader. To
+// create an empty blob, use size==0 and reader==nil.
+//
+// The API rejects requests with size > 256 MiB (but this limit is not
+// checked by the SDK). To write a larger blob, use CreateBlockBlob,
+// PutBlock, and PutBlockList.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Blob
+func (b *Blob) CreateBlockBlobFromReader(blob io.Reader, options *PutBlobOptions) error {
+ params := url.Values{}
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers["x-ms-blob-type"] = string(BlobTypeBlock)
+
+ headers["Content-Length"] = "0"
+ var n int64
+ var err error
+ if blob != nil {
+ buf := &bytes.Buffer{}
+ n, err = io.Copy(buf, blob)
+ if err != nil {
+ return err
+ }
+ blob = buf
+ headers["Content-Length"] = strconv.FormatInt(n, 10)
+ }
+ b.Properties.ContentLength = n
+
+ headers = mergeHeaders(headers, headersFromStruct(b.Properties))
+ headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, blob, b.Container.bsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusCreated})
+}
+
+// PutBlockOptions includes the options for a put block operation
+type PutBlockOptions struct {
+ Timeout uint
+ LeaseID string `header:"x-ms-lease-id"`
+ ContentMD5 string `header:"Content-MD5"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// PutBlock saves the given data chunk to the specified block blob with
+// given ID.
+//
+// The API rejects chunks larger than 100 MiB (but this limit is not
+// checked by the SDK).
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Block
+func (b *Blob) PutBlock(blockID string, chunk []byte, options *PutBlockOptions) error {
+ return b.PutBlockWithLength(blockID, uint64(len(chunk)), bytes.NewReader(chunk), options)
+}
+
+// PutBlockWithLength saves the given data stream of exactly specified size to
+// the block blob with given ID. It is an alternative to PutBlocks where data
+// comes as stream but the length is known in advance.
+//
+// The API rejects requests with size > 100 MiB (but this limit is not
+// checked by the SDK).
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Block
+func (b *Blob) PutBlockWithLength(blockID string, size uint64, blob io.Reader, options *PutBlockOptions) error {
+ query := url.Values{
+ "comp": {"block"},
+ "blockid": {blockID},
+ }
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers["Content-Length"] = fmt.Sprintf("%v", size)
+
+ if options != nil {
+ query = addTimeout(query, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), query)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, blob, b.Container.bsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusCreated})
+}
+
+// PutBlockListOptions includes the options for a put block list operation
+type PutBlockListOptions struct {
+ Timeout uint
+ LeaseID string `header:"x-ms-lease-id"`
+ IfModifiedSince *time.Time `header:"If-Modified-Since"`
+ IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
+ IfMatch string `header:"If-Match"`
+ IfNoneMatch string `header:"If-None-Match"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// PutBlockList saves list of blocks to the specified block blob.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Block-List
+func (b *Blob) PutBlockList(blocks []Block, options *PutBlockListOptions) error {
+ params := url.Values{"comp": {"blocklist"}}
+ blockListXML := prepareBlockListRequest(blocks)
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers["Content-Length"] = fmt.Sprintf("%v", len(blockListXML))
+ headers = mergeHeaders(headers, headersFromStruct(b.Properties))
+ headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, strings.NewReader(blockListXML), b.Container.bsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusCreated})
+}
+
+// GetBlockListOptions includes the options for a get block list operation
+type GetBlockListOptions struct {
+ Timeout uint
+ Snapshot *time.Time
+ LeaseID string `header:"x-ms-lease-id"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// GetBlockList retrieves list of blocks in the specified block blob.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Get-Block-List
+func (b *Blob) GetBlockList(blockType BlockListType, options *GetBlockListOptions) (BlockListResponse, error) {
+ params := url.Values{
+ "comp": {"blocklist"},
+ "blocklisttype": {string(blockType)},
+ }
+ headers := b.Container.bsc.client.getStandardHeaders()
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ params = addSnapshot(params, options.Snapshot)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ var out BlockListResponse
+ resp, err := b.Container.bsc.client.exec(http.MethodGet, uri, headers, nil, b.Container.bsc.auth)
+ if err != nil {
+ return out, err
+ }
+ defer resp.body.Close()
+
+ err = xmlUnmarshal(resp.body, &out)
+ return out, err
+}
diff --git a/vendor/github.com/Azure/azure-storage-go/client.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/client.go
similarity index 59%
rename from vendor/github.com/Azure/azure-storage-go/client.go
rename to vendor/github.com/Azure/azure-sdk-for-go/storage/client.go
index 9ddbf08ae..678eff036 100644
--- a/vendor/github.com/Azure/azure-storage-go/client.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/client.go
@@ -2,6 +2,7 @@
package storage
import (
+ "bufio"
"bytes"
"encoding/base64"
"encoding/json"
@@ -10,12 +11,16 @@ import (
"fmt"
"io"
"io/ioutil"
+ "mime"
+ "mime/multipart"
"net/http"
"net/url"
+ "regexp"
"runtime"
- "strconv"
"strings"
+ "time"
+ "github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
)
@@ -46,15 +51,60 @@ const (
storageEmulatorQueue = "127.0.0.1:10001"
userAgentHeader = "User-Agent"
+
+ userDefinedMetadataHeaderPrefix = "x-ms-meta-"
)
+var (
+ validStorageAccount = regexp.MustCompile("^[0-9a-z]{3,24}$")
+)
+
+// Sender sends a request
+type Sender interface {
+ Send(*Client, *http.Request) (*http.Response, error)
+}
+
+// DefaultSender is the default sender for the client. It implements
+// an automatic retry strategy.
+type DefaultSender struct {
+ RetryAttempts int
+ RetryDuration time.Duration
+ ValidStatusCodes []int
+ attempts int // used for testing
+}
+
+// Send is the default retry strategy in the client
+func (ds *DefaultSender) Send(c *Client, req *http.Request) (resp *http.Response, err error) {
+ rr := autorest.NewRetriableRequest(req)
+ for attempts := 0; attempts < ds.RetryAttempts; attempts++ {
+ err = rr.Prepare()
+ if err != nil {
+ return resp, err
+ }
+ resp, err = c.HTTPClient.Do(rr.Request())
+ if err != nil || !autorest.ResponseHasStatusCode(resp, ds.ValidStatusCodes...) {
+ return resp, err
+ }
+ autorest.DelayForBackoff(ds.RetryDuration, attempts, req.Cancel)
+ ds.attempts = attempts
+ }
+ ds.attempts++
+ return resp, err
+}
+
// Client is the object that needs to be constructed to perform
// operations on the storage account.
type Client struct {
// HTTPClient is the http.Client used to initiate API
- // requests. If it is nil, http.DefaultClient is used.
+ // requests. http.DefaultClient is used when creating a
+ // client.
HTTPClient *http.Client
+ // Sender is an interface that sends the request. Clients are
+ // created with a DefaultSender. The DefaultSender has an
+ // automatic retry strategy built in. The Sender can be customized.
+ Sender Sender
+
accountName string
accountKey []byte
useHTTPS bool
@@ -72,7 +122,7 @@ type storageResponse struct {
type odataResponse struct {
storageResponse
- odata odataErrorMessage
+ odata odataErrorWrapper
}
// AzureStorageServiceError contains fields of the error response from
@@ -85,22 +135,25 @@ type AzureStorageServiceError struct {
QueryParameterName string `xml:"QueryParameterName"`
QueryParameterValue string `xml:"QueryParameterValue"`
Reason string `xml:"Reason"`
+ Lang string
StatusCode int
RequestID string
+ Date string
+ APIVersion string
}
-type odataErrorMessageMessage struct {
+type odataErrorMessage struct {
Lang string `json:"lang"`
Value string `json:"value"`
}
-type odataErrorMessageInternal struct {
- Code string `json:"code"`
- Message odataErrorMessageMessage `json:"message"`
+type odataError struct {
+ Code string `json:"code"`
+ Message odataErrorMessage `json:"message"`
}
-type odataErrorMessage struct {
- Err odataErrorMessageInternal `json:"odata.error"`
+type odataErrorWrapper struct {
+ Err odataError `json:"odata.error"`
}
// UnexpectedStatusCodeError is returned when a storage service responds with neither an error
@@ -155,8 +208,8 @@ func NewEmulatorClient() (Client, error) {
// storage endpoint than Azure Public Cloud.
func NewClient(accountName, accountKey, blobServiceBaseURL, apiVersion string, useHTTPS bool) (Client, error) {
var c Client
- if accountName == "" {
- return c, fmt.Errorf("azure: account name required")
+ if !IsValidStorageAccount(accountName) {
+ return c, fmt.Errorf("azure: account name is not valid: it must be between 3 and 24 characters, and only may contain numbers and lowercase letters: %v", accountName)
} else if accountKey == "" {
return c, fmt.Errorf("azure: account key required")
} else if blobServiceBaseURL == "" {
@@ -169,19 +222,37 @@ func NewClient(accountName, accountKey, blobServiceBaseURL, apiVersion string, u
}
c = Client{
+ HTTPClient: http.DefaultClient,
accountName: accountName,
accountKey: key,
useHTTPS: useHTTPS,
baseURL: blobServiceBaseURL,
apiVersion: apiVersion,
UseSharedKeyLite: false,
+ Sender: &DefaultSender{
+ RetryAttempts: 5,
+ ValidStatusCodes: []int{
+ http.StatusRequestTimeout, // 408
+ http.StatusInternalServerError, // 500
+ http.StatusBadGateway, // 502
+ http.StatusServiceUnavailable, // 503
+ http.StatusGatewayTimeout, // 504
+ },
+ RetryDuration: time.Second * 5,
+ },
}
c.userAgent = c.getDefaultUserAgent()
return c, nil
}
+// IsValidStorageAccount checks if the storage account name is valid.
+// See https://docs.microsoft.com/en-us/azure/storage/storage-create-storage-account
+func IsValidStorageAccount(account string) bool {
+ return validStorageAccount.MatchString(account)
+}
+
func (c Client) getDefaultUserAgent() string {
- return fmt.Sprintf("Go/%s (%s-%s) Azure-SDK-For-Go/%s storage-dataplane/%s",
+ return fmt.Sprintf("Go/%s (%s-%s) azure-storage-go/%s api-version/%s",
runtime.Version(),
runtime.GOARCH,
runtime.GOOS,
@@ -210,7 +281,7 @@ func (c *Client) protectUserAgent(extraheaders map[string]string) map[string]str
return extraheaders
}
-func (c Client) getBaseURL(service string) string {
+func (c Client) getBaseURL(service string) *url.URL {
scheme := "http"
if c.useHTTPS {
scheme = "https"
@@ -229,18 +300,14 @@ func (c Client) getBaseURL(service string) string {
host = fmt.Sprintf("%s.%s.%s", c.accountName, service, c.baseURL)
}
- u := &url.URL{
+ return &url.URL{
Scheme: scheme,
- Host: host}
- return u.String()
+ Host: host,
+ }
}
func (c Client) getEndpoint(service, path string, params url.Values) string {
- u, err := url.Parse(c.getBaseURL(service))
- if err != nil {
- // really should not be happening
- panic(err)
- }
+ u := c.getBaseURL(service)
// API doesn't accept path segments not starting with '/'
if !strings.HasPrefix(path, "/") {
@@ -331,46 +398,44 @@ func (c Client) exec(verb, url string, headers map[string]string, body io.Reader
return nil, errors.New("azure/storage: error creating request: " + err.Error())
}
- if clstr, ok := headers["Content-Length"]; ok {
- // content length header is being signed, but completely ignored by golang.
- // instead we have to use the ContentLength property on the request struct
- // (see https://golang.org/src/net/http/request.go?s=18140:18370#L536 and
- // https://golang.org/src/net/http/transfer.go?s=1739:2467#L49)
- req.ContentLength, err = strconv.ParseInt(clstr, 10, 64)
- if err != nil {
- return nil, err
- }
- }
for k, v := range headers {
req.Header.Add(k, v)
}
- httpClient := c.HTTPClient
- if httpClient == nil {
- httpClient = http.DefaultClient
- }
- resp, err := httpClient.Do(req)
+ resp, err := c.Sender.Send(&c, req)
if err != nil {
return nil, err
}
- statusCode := resp.StatusCode
- if statusCode >= 400 && statusCode <= 505 {
+ if resp.StatusCode >= 400 && resp.StatusCode <= 505 {
var respBody []byte
respBody, err = readAndCloseBody(resp.Body)
if err != nil {
return nil, err
}
- requestID := resp.Header.Get("x-ms-request-id")
+ requestID, date, version := getDebugHeaders(resp.Header)
if len(respBody) == 0 {
// no error in response body, might happen in HEAD requests
- err = serviceErrFromStatusCode(resp.StatusCode, resp.Status, requestID)
+ err = serviceErrFromStatusCode(resp.StatusCode, resp.Status, requestID, date, version)
} else {
+ storageErr := AzureStorageServiceError{
+ StatusCode: resp.StatusCode,
+ RequestID: requestID,
+ Date: date,
+ APIVersion: version,
+ }
// response contains storage service error object, unmarshal
- storageErr, errIn := serviceErrFromXML(respBody, resp.StatusCode, requestID)
- if err != nil { // error unmarshaling the error response
- err = errIn
+ if resp.Header.Get("Content-Type") == "application/xml" {
+ errIn := serviceErrFromXML(respBody, &storageErr)
+ if err != nil { // error unmarshaling the error response
+ err = errIn
+ }
+ } else {
+ errIn := serviceErrFromJSON(respBody, &storageErr)
+ if err != nil { // error unmarshaling the error response
+ err = errIn
+ }
}
err = storageErr
}
@@ -387,10 +452,10 @@ func (c Client) exec(verb, url string, headers map[string]string, body io.Reader
body: resp.Body}, nil
}
-func (c Client) execInternalJSON(verb, url string, headers map[string]string, body io.Reader, auth authentication) (*odataResponse, error) {
+func (c Client) execInternalJSONCommon(verb, url string, headers map[string]string, body io.Reader, auth authentication) (*odataResponse, *http.Request, *http.Response, error) {
headers, err := c.addAuthorizationHeader(verb, url, headers, auth)
if err != nil {
- return nil, err
+ return nil, nil, nil, err
}
req, err := http.NewRequest(verb, url, body)
@@ -398,14 +463,9 @@ func (c Client) execInternalJSON(verb, url string, headers map[string]string, bo
req.Header.Add(k, v)
}
- httpClient := c.HTTPClient
- if httpClient == nil {
- httpClient = http.DefaultClient
- }
-
- resp, err := httpClient.Do(req)
+ resp, err := c.Sender.Send(&c, req)
if err != nil {
- return nil, err
+ return nil, nil, nil, err
}
respToRet := &odataResponse{}
@@ -418,22 +478,110 @@ func (c Client) execInternalJSON(verb, url string, headers map[string]string, bo
var respBody []byte
respBody, err = readAndCloseBody(resp.Body)
if err != nil {
- return nil, err
+ return nil, nil, nil, err
}
+ requestID, date, version := getDebugHeaders(resp.Header)
if len(respBody) == 0 {
// no error in response body, might happen in HEAD requests
- err = serviceErrFromStatusCode(resp.StatusCode, resp.Status, resp.Header.Get("x-ms-request-id"))
- return respToRet, err
+ err = serviceErrFromStatusCode(resp.StatusCode, resp.Status, requestID, date, version)
+ return respToRet, req, resp, err
}
// try unmarshal as odata.error json
err = json.Unmarshal(respBody, &respToRet.odata)
- return respToRet, err
+ }
+
+ return respToRet, req, resp, err
+}
+
+func (c Client) execInternalJSON(verb, url string, headers map[string]string, body io.Reader, auth authentication) (*odataResponse, error) {
+ respToRet, _, _, err := c.execInternalJSONCommon(verb, url, headers, body, auth)
+ return respToRet, err
+}
+
+func (c Client) execBatchOperationJSON(verb, url string, headers map[string]string, body io.Reader, auth authentication) (*odataResponse, error) {
+ // execute common query, get back generated request, response etc... for more processing.
+ respToRet, req, resp, err := c.execInternalJSONCommon(verb, url, headers, body, auth)
+ if err != nil {
+ return nil, err
+ }
+
+ // return the OData in the case of executing batch commands.
+ // In this case we need to read the outer batch boundary and contents.
+ // Then we read the changeset information within the batch
+ var respBody []byte
+ respBody, err = readAndCloseBody(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ // outer multipart body
+ _, batchHeader, err := mime.ParseMediaType(resp.Header["Content-Type"][0])
+ if err != nil {
+ return nil, err
+ }
+
+ // batch details.
+ batchBoundary := batchHeader["boundary"]
+ batchPartBuf, changesetBoundary, err := genBatchReader(batchBoundary, respBody)
+ if err != nil {
+ return nil, err
+ }
+
+ // changeset details.
+ err = genChangesetReader(req, respToRet, batchPartBuf, changesetBoundary)
+ if err != nil {
+ return nil, err
}
return respToRet, nil
}
+func genChangesetReader(req *http.Request, respToRet *odataResponse, batchPartBuf io.Reader, changesetBoundary string) error {
+ changesetMultiReader := multipart.NewReader(batchPartBuf, changesetBoundary)
+ changesetPart, err := changesetMultiReader.NextPart()
+ if err != nil {
+ return err
+ }
+
+ changesetPartBufioReader := bufio.NewReader(changesetPart)
+ changesetResp, err := http.ReadResponse(changesetPartBufioReader, req)
+ if err != nil {
+ return err
+ }
+
+ if changesetResp.StatusCode != http.StatusNoContent {
+ changesetBody, err := readAndCloseBody(changesetResp.Body)
+ err = json.Unmarshal(changesetBody, &respToRet.odata)
+ if err != nil {
+ return err
+ }
+ respToRet.statusCode = changesetResp.StatusCode
+ }
+
+ return nil
+}
+
+func genBatchReader(batchBoundary string, respBody []byte) (io.Reader, string, error) {
+ respBodyString := string(respBody)
+ respBodyReader := strings.NewReader(respBodyString)
+
+ // reading batchresponse
+ batchMultiReader := multipart.NewReader(respBodyReader, batchBoundary)
+ batchPart, err := batchMultiReader.NextPart()
+ if err != nil {
+ return nil, "", err
+ }
+ batchPartBufioReader := bufio.NewReader(batchPart)
+
+ _, changesetHeader, err := mime.ParseMediaType(batchPart.Header.Get("Content-Type"))
+ if err != nil {
+ return nil, "", err
+ }
+ changesetBoundary := changesetHeader["boundary"]
+ return batchPartBufioReader, changesetBoundary, nil
+}
+
func readAndCloseBody(body io.ReadCloser) ([]byte, error) {
defer body.Close()
out, err := ioutil.ReadAll(body)
@@ -443,28 +591,40 @@ func readAndCloseBody(body io.ReadCloser) ([]byte, error) {
return out, err
}
-func serviceErrFromXML(body []byte, statusCode int, requestID string) (AzureStorageServiceError, error) {
- var storageErr AzureStorageServiceError
- if err := xml.Unmarshal(body, &storageErr); err != nil {
- return storageErr, err
+func serviceErrFromXML(body []byte, storageErr *AzureStorageServiceError) error {
+ if err := xml.Unmarshal(body, storageErr); err != nil {
+ storageErr.Message = fmt.Sprintf("Response body could no be unmarshaled: %v. Body: %v.", err, string(body))
+ return err
}
- storageErr.StatusCode = statusCode
- storageErr.RequestID = requestID
- return storageErr, nil
+ return nil
}
-func serviceErrFromStatusCode(code int, status string, requestID string) AzureStorageServiceError {
+func serviceErrFromJSON(body []byte, storageErr *AzureStorageServiceError) error {
+ odataError := odataErrorWrapper{}
+ if err := json.Unmarshal(body, &odataError); err != nil {
+ storageErr.Message = fmt.Sprintf("Response body could no be unmarshaled: %v. Body: %v.", err, string(body))
+ return err
+ }
+ storageErr.Code = odataError.Err.Code
+ storageErr.Message = odataError.Err.Message.Value
+ storageErr.Lang = odataError.Err.Message.Lang
+ return nil
+}
+
+func serviceErrFromStatusCode(code int, status string, requestID, date, version string) AzureStorageServiceError {
return AzureStorageServiceError{
StatusCode: code,
Code: status,
RequestID: requestID,
+ Date: date,
+ APIVersion: version,
Message: "no response body was available for error status code",
}
}
func (e AzureStorageServiceError) Error() string {
- return fmt.Sprintf("storage: service returned error: StatusCode=%d, ErrorCode=%s, ErrorMessage=%s, RequestId=%s, QueryParameterName=%s, QueryParameterValue=%s",
- e.StatusCode, e.Code, e.Message, e.RequestID, e.QueryParameterName, e.QueryParameterValue)
+ return fmt.Sprintf("storage: service returned error: StatusCode=%d, ErrorCode=%s, ErrorMessage=%s, RequestInitiated=%s, RequestId=%s, API Version=%s, QueryParameterName=%s, QueryParameterValue=%s",
+ e.StatusCode, e.Code, e.Message, e.Date, e.RequestID, e.APIVersion, e.QueryParameterName, e.QueryParameterValue)
}
// checkRespCode returns UnexpectedStatusError if the given response code is not
@@ -477,3 +637,18 @@ func checkRespCode(respCode int, allowed []int) error {
}
return UnexpectedStatusCodeError{allowed, respCode}
}
+
+func (c Client) addMetadataToHeaders(h map[string]string, metadata map[string]string) map[string]string {
+ metadata = c.protectUserAgent(metadata)
+ for k, v := range metadata {
+ h[userDefinedMetadataHeaderPrefix+k] = v
+ }
+ return h
+}
+
+func getDebugHeaders(h http.Header) (requestID, date, version string) {
+ requestID = h.Get("x-ms-request-id")
+ version = h.Get("x-ms-version")
+ date = h.Get("Date")
+ return
+}
diff --git a/vendor/github.com/Azure/azure-storage-go/container.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/container.go
similarity index 67%
rename from vendor/github.com/Azure/azure-storage-go/container.go
rename to vendor/github.com/Azure/azure-sdk-for-go/storage/container.go
index f06423967..c2c9c055b 100644
--- a/vendor/github.com/Azure/azure-storage-go/container.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/container.go
@@ -8,6 +8,7 @@ import (
"net/http"
"net/url"
"strconv"
+ "strings"
"time"
)
@@ -16,6 +17,7 @@ type Container struct {
bsc *BlobStorageClient
Name string `xml:"Name"`
Properties ContainerProperties `xml:"Properties"`
+ Metadata map[string]string
}
func (c *Container) buildPath() string {
@@ -69,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.
//
@@ -77,9 +87,10 @@ type ListBlobsParameters struct {
Prefix string
Delimiter string
Marker string
- Include string
+ Include *IncludeBlobDataset
MaxResults uint
Timeout uint
+ RequestID string
}
func (p ListBlobsParameters) getParameters() url.Values {
@@ -94,19 +105,32 @@ 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", fmt.Sprintf("%v", p.MaxResults))
+ out.Set("maxresults", strconv.FormatUint(uint64(p.MaxResults), 10))
}
if p.Timeout != 0 {
- out.Set("timeout", fmt.Sprintf("%v", p.Timeout))
+ out.Set("timeout", strconv.FormatUint(uint64(p.Timeout), 10))
}
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.
//
@@ -142,23 +166,38 @@ const (
ContainerAccessHeader string = "x-ms-blob-public-access"
)
+// GetBlobReference returns a Blob object for the specified blob name.
+func (c *Container) GetBlobReference(name string) *Blob {
+ return &Blob{
+ Container: c,
+ Name: name,
+ }
+}
+
+// CreateContainerOptions includes the options for a create container operation
+type CreateContainerOptions struct {
+ Timeout uint
+ Access ContainerAccessType `header:"x-ms-blob-public-access"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
// Create creates a blob container within the storage account
// with given name and access level. Returns error if container already exists.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179468.aspx
-func (c *Container) Create() error {
- resp, err := c.create()
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Create-Container
+func (c *Container) Create(options *CreateContainerOptions) error {
+ resp, err := c.create(options)
if err != nil {
return err
}
- defer readAndCloseBody(resp.body)
+ readAndCloseBody(resp.body)
return checkRespCode(resp.statusCode, []int{http.StatusCreated})
}
// CreateIfNotExists creates a blob container if it does not exist. Returns
// true if container is newly created or false if container already exists.
-func (c *Container) CreateIfNotExists() (bool, error) {
- resp, err := c.create()
+func (c *Container) CreateIfNotExists(options *CreateContainerOptions) (bool, error) {
+ resp, err := c.create(options)
if resp != nil {
defer readAndCloseBody(resp.body)
if resp.statusCode == http.StatusCreated || resp.statusCode == http.StatusConflict {
@@ -168,9 +207,17 @@ func (c *Container) CreateIfNotExists() (bool, error) {
return false, err
}
-func (c *Container) create() (*storageResponse, error) {
- uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), url.Values{"restype": {"container"}})
+func (c *Container) create(options *CreateContainerOptions) (*storageResponse, error) {
+ query := url.Values{"restype": {"container"}}
headers := c.bsc.client.getStandardHeaders()
+ headers = c.bsc.client.addMetadataToHeaders(headers, c.Metadata)
+
+ if options != nil {
+ query = addTimeout(query, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), query)
+
return c.bsc.client.exec(http.MethodPut, uri, headers, nil, c.bsc.auth)
}
@@ -190,30 +237,36 @@ func (c *Container) Exists() (bool, error) {
return false, err
}
-// SetPermissions sets up container permissions as per https://msdn.microsoft.com/en-us/library/azure/dd179391.aspx
-func (c *Container) SetPermissions(permissions ContainerPermissions, timeout int, leaseID string) error {
+// SetContainerPermissionOptions includes options for a set container permissions operation
+type SetContainerPermissionOptions struct {
+ Timeout uint
+ LeaseID string `header:"x-ms-lease-id"`
+ IfModifiedSince *time.Time `header:"If-Modified-Since"`
+ IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// SetPermissions sets up container permissions
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Set-Container-ACL
+func (c *Container) SetPermissions(permissions ContainerPermissions, options *SetContainerPermissionOptions) error {
+ body, length, err := generateContainerACLpayload(permissions.AccessPolicies)
+ if err != nil {
+ return err
+ }
params := url.Values{
"restype": {"container"},
"comp": {"acl"},
}
-
- if timeout > 0 {
- params.Add("timeout", strconv.Itoa(timeout))
- }
-
- uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), params)
headers := c.bsc.client.getStandardHeaders()
- if permissions.AccessType != "" {
- headers[ContainerAccessHeader] = string(permissions.AccessType)
- }
-
- if leaseID != "" {
- headers[headerLeaseID] = leaseID
- }
-
- body, length, err := generateContainerACLpayload(permissions.AccessPolicies)
+ headers = addToHeaders(headers, ContainerAccessHeader, string(permissions.AccessType))
headers["Content-Length"] = strconv.Itoa(length)
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), params)
+
resp, err := c.bsc.client.exec(http.MethodPut, uri, headers, body, c.bsc.auth)
if err != nil {
return err
@@ -227,25 +280,28 @@ func (c *Container) SetPermissions(permissions ContainerPermissions, timeout int
return nil
}
+// GetContainerPermissionOptions includes options for a get container permissions operation
+type GetContainerPermissionOptions struct {
+ Timeout uint
+ LeaseID string `header:"x-ms-lease-id"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
// GetPermissions gets the container permissions as per https://msdn.microsoft.com/en-us/library/azure/dd179469.aspx
// If timeout is 0 then it will not be passed to Azure
// leaseID will only be passed to Azure if populated
-func (c *Container) GetPermissions(timeout int, leaseID string) (*ContainerPermissions, error) {
+func (c *Container) GetPermissions(options *GetContainerPermissionOptions) (*ContainerPermissions, error) {
params := url.Values{
"restype": {"container"},
"comp": {"acl"},
}
-
- if timeout > 0 {
- params.Add("timeout", strconv.Itoa(timeout))
- }
-
- uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), params)
headers := c.bsc.client.getStandardHeaders()
- if leaseID != "" {
- headers[headerLeaseID] = leaseID
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
}
+ uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), params)
resp, err := c.bsc.client.exec(http.MethodGet, uri, headers, nil, c.bsc.auth)
if err != nil {
@@ -284,16 +340,25 @@ func buildAccessPolicy(ap AccessPolicy, headers *http.Header) *ContainerPermissi
return &permissions
}
+// DeleteContainerOptions includes options for a delete container operation
+type DeleteContainerOptions struct {
+ Timeout uint
+ LeaseID string `header:"x-ms-lease-id"`
+ IfModifiedSince *time.Time `header:"If-Modified-Since"`
+ IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
// Delete deletes the container with given name on the storage
// account. If the container does not exist returns error.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179408.aspx
-func (c *Container) Delete() error {
- resp, err := c.delete()
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/delete-container
+func (c *Container) Delete(options *DeleteContainerOptions) error {
+ resp, err := c.delete(options)
if err != nil {
return err
}
- defer readAndCloseBody(resp.body)
+ readAndCloseBody(resp.body)
return checkRespCode(resp.statusCode, []int{http.StatusAccepted})
}
@@ -302,9 +367,9 @@ func (c *Container) Delete() error {
// false if the container did not exist at the time of the Delete Container
// operation.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179408.aspx
-func (c *Container) DeleteIfExists() (bool, error) {
- resp, err := c.delete()
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/delete-container
+func (c *Container) DeleteIfExists(options *DeleteContainerOptions) (bool, error) {
+ resp, err := c.delete(options)
if resp != nil {
defer readAndCloseBody(resp.body)
if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound {
@@ -314,23 +379,32 @@ func (c *Container) DeleteIfExists() (bool, error) {
return false, err
}
-func (c *Container) delete() (*storageResponse, error) {
- uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), url.Values{"restype": {"container"}})
+func (c *Container) delete(options *DeleteContainerOptions) (*storageResponse, error) {
+ query := url.Values{"restype": {"container"}}
headers := c.bsc.client.getStandardHeaders()
+
+ if options != nil {
+ query = addTimeout(query, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), query)
+
return c.bsc.client.exec(http.MethodDelete, uri, headers, nil, c.bsc.auth)
}
// ListBlobs returns an object that contains list of blobs in the container,
// pagination token and other information in the response of List Blobs call.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dd135734.aspx
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/List-Blobs
func (c *Container) ListBlobs(params ListBlobsParameters) (BlobListResponse, error) {
q := mergeParams(params.getParameters(), url.Values{
"restype": {"container"},
"comp": {"list"}},
)
uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), q)
+
headers := c.bsc.client.getStandardHeaders()
+ headers = addToHeaders(headers, "x-ms-client-request-id", params.RequestID)
var out BlobListResponse
resp, err := c.bsc.client.exec(http.MethodGet, uri, headers, nil, c.bsc.auth)
@@ -340,6 +414,9 @@ func (c *Container) ListBlobs(params ListBlobsParameters) (BlobListResponse, err
defer resp.body.Close()
err = xmlUnmarshal(resp.body, &out)
+ for i := range out.Blobs {
+ out.Blobs[i].Container = c
+ }
return out, err
}
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/copyblob.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/copyblob.go
new file mode 100644
index 000000000..377a3c622
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/copyblob.go
@@ -0,0 +1,223 @@
+package storage
+
+import (
+ "errors"
+ "fmt"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+)
+
+const (
+ blobCopyStatusPending = "pending"
+ blobCopyStatusSuccess = "success"
+ blobCopyStatusAborted = "aborted"
+ blobCopyStatusFailed = "failed"
+)
+
+// CopyOptions includes the options for a copy blob operation
+type CopyOptions struct {
+ Timeout uint
+ Source CopyOptionsConditions
+ Destiny CopyOptionsConditions
+ RequestID string
+}
+
+// IncrementalCopyOptions includes the options for an incremental copy blob operation
+type IncrementalCopyOptions struct {
+ Timeout uint
+ Destination IncrementalCopyOptionsConditions
+ RequestID string
+}
+
+// CopyOptionsConditions includes some conditional options in a copy blob operation
+type CopyOptionsConditions struct {
+ LeaseID string
+ IfModifiedSince *time.Time
+ IfUnmodifiedSince *time.Time
+ IfMatch string
+ IfNoneMatch string
+}
+
+// IncrementalCopyOptionsConditions includes some conditional options in a copy blob operation
+type IncrementalCopyOptionsConditions struct {
+ IfModifiedSince *time.Time
+ IfUnmodifiedSince *time.Time
+ IfMatch string
+ IfNoneMatch string
+}
+
+// Copy starts a blob copy operation and waits for the operation to
+// complete. sourceBlob parameter must be a canonical URL to the blob (can be
+// obtained using GetBlobURL method.) There is no SLA on blob copy and therefore
+// this helper method works faster on smaller files.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Copy-Blob
+func (b *Blob) Copy(sourceBlob string, options *CopyOptions) error {
+ copyID, err := b.StartCopy(sourceBlob, options)
+ if err != nil {
+ return err
+ }
+
+ return b.WaitForCopy(copyID)
+}
+
+// StartCopy starts a blob copy operation.
+// sourceBlob parameter must be a canonical URL to the blob (can be
+// obtained using GetBlobURL method.)
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Copy-Blob
+func (b *Blob) StartCopy(sourceBlob string, options *CopyOptions) (string, error) {
+ params := url.Values{}
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers["x-ms-copy-source"] = sourceBlob
+ headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = addToHeaders(headers, "x-ms-client-request-id", options.RequestID)
+ // source
+ headers = addToHeaders(headers, "x-ms-source-lease-id", options.Source.LeaseID)
+ headers = addTimeToHeaders(headers, "x-ms-source-if-modified-since", options.Source.IfModifiedSince)
+ headers = addTimeToHeaders(headers, "x-ms-source-if-unmodified-since", options.Source.IfUnmodifiedSince)
+ headers = addToHeaders(headers, "x-ms-source-if-match", options.Source.IfMatch)
+ headers = addToHeaders(headers, "x-ms-source-if-none-match", options.Source.IfNoneMatch)
+ //destiny
+ headers = addToHeaders(headers, "x-ms-lease-id", options.Destiny.LeaseID)
+ headers = addTimeToHeaders(headers, "x-ms-if-modified-since", options.Destiny.IfModifiedSince)
+ headers = addTimeToHeaders(headers, "x-ms-if-unmodified-since", options.Destiny.IfUnmodifiedSince)
+ headers = addToHeaders(headers, "x-ms-if-match", options.Destiny.IfMatch)
+ headers = addToHeaders(headers, "x-ms-if-none-match", options.Destiny.IfNoneMatch)
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
+ if err != nil {
+ return "", err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err := checkRespCode(resp.statusCode, []int{http.StatusAccepted, http.StatusCreated}); err != nil {
+ return "", err
+ }
+
+ copyID := resp.headers.Get("x-ms-copy-id")
+ if copyID == "" {
+ return "", errors.New("Got empty copy id header")
+ }
+ return copyID, nil
+}
+
+// AbortCopyOptions includes the options for an abort blob operation
+type AbortCopyOptions struct {
+ Timeout uint
+ LeaseID string `header:"x-ms-lease-id"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// AbortCopy aborts a BlobCopy which has already been triggered by the StartBlobCopy function.
+// copyID is generated from StartBlobCopy function.
+// currentLeaseID is required IF the destination blob has an active lease on it.
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Abort-Copy-Blob
+func (b *Blob) AbortCopy(copyID string, options *AbortCopyOptions) error {
+ params := url.Values{
+ "comp": {"copy"},
+ "copyid": {copyID},
+ }
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers["x-ms-copy-action"] = "abort"
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
+}
+
+// WaitForCopy loops until a BlobCopy operation is completed (or fails with error)
+func (b *Blob) WaitForCopy(copyID string) error {
+ for {
+ err := b.GetProperties(nil)
+ if err != nil {
+ return err
+ }
+
+ if b.Properties.CopyID != copyID {
+ return errBlobCopyIDMismatch
+ }
+
+ switch b.Properties.CopyStatus {
+ case blobCopyStatusSuccess:
+ return nil
+ case blobCopyStatusPending:
+ continue
+ case blobCopyStatusAborted:
+ return errBlobCopyAborted
+ case blobCopyStatusFailed:
+ return fmt.Errorf("storage: blob copy failed. Id=%s Description=%s", b.Properties.CopyID, b.Properties.CopyStatusDescription)
+ default:
+ return fmt.Errorf("storage: unhandled blob copy status: '%s'", b.Properties.CopyStatus)
+ }
+ }
+}
+
+// IncrementalCopyBlob copies a snapshot of a source blob and copies to referring blob
+// sourceBlob parameter must be a valid snapshot URL of the original blob.
+// THe original blob mut be public, or use a Shared Access Signature.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/incremental-copy-blob .
+func (b *Blob) IncrementalCopyBlob(sourceBlobURL string, snapshotTime time.Time, options *IncrementalCopyOptions) (string, error) {
+ params := url.Values{"comp": {"incrementalcopy"}}
+
+ // need formatting to 7 decimal places so it's friendly to Windows and *nix
+ snapshotTimeFormatted := snapshotTime.Format("2006-01-02T15:04:05.0000000Z")
+ u, err := url.Parse(sourceBlobURL)
+ if err != nil {
+ return "", err
+ }
+ query := u.Query()
+ query.Add("snapshot", snapshotTimeFormatted)
+ encodedQuery := query.Encode()
+ encodedQuery = strings.Replace(encodedQuery, "%3A", ":", -1)
+ u.RawQuery = encodedQuery
+ snapshotURL := u.String()
+
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers["x-ms-copy-source"] = snapshotURL
+
+ if options != nil {
+ addTimeout(params, options.Timeout)
+ headers = addToHeaders(headers, "x-ms-client-request-id", options.RequestID)
+ headers = addTimeToHeaders(headers, "x-ms-if-modified-since", options.Destination.IfModifiedSince)
+ headers = addTimeToHeaders(headers, "x-ms-if-unmodified-since", options.Destination.IfUnmodifiedSince)
+ headers = addToHeaders(headers, "x-ms-if-match", options.Destination.IfMatch)
+ headers = addToHeaders(headers, "x-ms-if-none-match", options.Destination.IfNoneMatch)
+ }
+
+ // get URI of destination blob
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
+ if err != nil {
+ return "", err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err := checkRespCode(resp.statusCode, []int{http.StatusAccepted}); err != nil {
+ return "", err
+ }
+
+ copyID := resp.headers.Get("x-ms-copy-id")
+ if copyID == "" {
+ return "", errors.New("Got empty copy id header")
+ }
+ return copyID, nil
+}
diff --git a/vendor/github.com/Azure/azure-storage-go/directory.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/directory.go
similarity index 75%
rename from vendor/github.com/Azure/azure-storage-go/directory.go
rename to vendor/github.com/Azure/azure-sdk-for-go/storage/directory.go
index d27e62079..29610329e 100644
--- a/vendor/github.com/Azure/azure-storage-go/directory.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/directory.go
@@ -25,8 +25,9 @@ type DirectoryProperties struct {
// ListDirsAndFilesParameters defines the set of customizable parameters to
// make a List Files and Directories call.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn166980.aspx
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/List-Directories-and-Files
type ListDirsAndFilesParameters struct {
+ Prefix string
Marker string
MaxResults uint
Timeout uint
@@ -35,7 +36,7 @@ type ListDirsAndFilesParameters struct {
// DirsAndFilesListResponse contains the response fields from
// a List Files and Directories call.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn166980.aspx
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/List-Directories-and-Files
type DirsAndFilesListResponse struct {
XMLName xml.Name `xml:"EnumerationResults"`
Xmlns string `xml:"xmlns,attr"`
@@ -60,14 +61,15 @@ func (d *Directory) buildPath() string {
// Create this directory in the associated share.
// If a directory with the same name already exists, the operation fails.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn166993.aspx
-func (d *Directory) Create() error {
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Create-Directory
+func (d *Directory) Create(options *FileRequestOptions) error {
// if this is the root directory exit early
if d.parent == nil {
return nil
}
- headers, err := d.fsc.createResource(d.buildPath(), resourceDirectory, nil, mergeMDIntoExtraHeaders(d.Metadata, nil), []int{http.StatusCreated})
+ params := prepareOptions(options)
+ headers, err := d.fsc.createResource(d.buildPath(), resourceDirectory, params, mergeMDIntoExtraHeaders(d.Metadata, nil), []int{http.StatusCreated})
if err != nil {
return err
}
@@ -80,14 +82,15 @@ func (d *Directory) Create() error {
// directory does not exists. Returns true if the directory is newly created or
// false if the directory already exists.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn166993.aspx
-func (d *Directory) CreateIfNotExists() (bool, error) {
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Create-Directory
+func (d *Directory) CreateIfNotExists(options *FileRequestOptions) (bool, error) {
// if this is the root directory exit early
if d.parent == nil {
return false, nil
}
- resp, err := d.fsc.createResourceNoClose(d.buildPath(), resourceDirectory, nil, nil)
+ params := prepareOptions(options)
+ resp, err := d.fsc.createResourceNoClose(d.buildPath(), resourceDirectory, params, nil)
if resp != nil {
defer readAndCloseBody(resp.body)
if resp.statusCode == http.StatusCreated || resp.statusCode == http.StatusConflict {
@@ -96,7 +99,7 @@ func (d *Directory) CreateIfNotExists() (bool, error) {
return true, nil
}
- return false, d.FetchAttributes()
+ return false, d.FetchAttributes(nil)
}
}
@@ -106,16 +109,16 @@ func (d *Directory) CreateIfNotExists() (bool, error) {
// Delete removes this directory. It must be empty in order to be deleted.
// If the directory does not exist the operation fails.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn166969.aspx
-func (d *Directory) Delete() error {
- return d.fsc.deleteResource(d.buildPath(), resourceDirectory)
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Delete-Directory
+func (d *Directory) Delete(options *FileRequestOptions) error {
+ return d.fsc.deleteResource(d.buildPath(), resourceDirectory, options)
}
// DeleteIfExists removes this directory if it exists.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn166969.aspx
-func (d *Directory) DeleteIfExists() (bool, error) {
- resp, err := d.fsc.deleteResourceNoClose(d.buildPath(), resourceDirectory)
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Delete-Directory
+func (d *Directory) DeleteIfExists(options *FileRequestOptions) (bool, error) {
+ resp, err := d.fsc.deleteResourceNoClose(d.buildPath(), resourceDirectory, options)
if resp != nil {
defer readAndCloseBody(resp.body)
if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound {
@@ -135,8 +138,10 @@ func (d *Directory) Exists() (bool, error) {
}
// FetchAttributes retrieves metadata for this directory.
-func (d *Directory) FetchAttributes() error {
- headers, err := d.fsc.getResourceHeaders(d.buildPath(), compNone, resourceDirectory, http.MethodHead)
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-directory-properties
+func (d *Directory) FetchAttributes(options *FileRequestOptions) error {
+ params := prepareOptions(options)
+ headers, err := d.fsc.getResourceHeaders(d.buildPath(), compNone, resourceDirectory, params, http.MethodHead)
if err != nil {
return err
}
@@ -170,7 +175,7 @@ func (d *Directory) GetFileReference(name string) *File {
// ListDirsAndFiles returns a list of files and directories under this directory.
// It also contains a pagination token and other response details.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn166980.aspx
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/List-Directories-and-Files
func (d *Directory) ListDirsAndFiles(params ListDirsAndFilesParameters) (*DirsAndFilesListResponse, error) {
q := mergeParams(params.getParameters(), getURLInitValues(compList, resourceDirectory))
@@ -192,9 +197,9 @@ func (d *Directory) ListDirsAndFiles(params ListDirsAndFilesParameters) (*DirsAn
// are case-insensitive so case munging should not matter to other
// applications either.
//
-// See https://msdn.microsoft.com/en-us/library/azure/mt427370.aspx
-func (d *Directory) SetMetadata() error {
- headers, err := d.fsc.setResourceHeaders(d.buildPath(), compMetadata, resourceDirectory, mergeMDIntoExtraHeaders(d.Metadata, nil))
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Set-Directory-Metadata
+func (d *Directory) SetMetadata(options *FileRequestOptions) error {
+ headers, err := d.fsc.setResourceHeaders(d.buildPath(), compMetadata, resourceDirectory, mergeMDIntoExtraHeaders(d.Metadata, nil), options)
if err != nil {
return err
}
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/entity.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/entity.go
new file mode 100644
index 000000000..13e947507
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/entity.go
@@ -0,0 +1,439 @@
+package storage
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/satori/uuid"
+)
+
+// Annotating as secure for gas scanning
+/* #nosec */
+const (
+ partitionKeyNode = "PartitionKey"
+ rowKeyNode = "RowKey"
+ etagErrorTemplate = "Etag didn't match: %v"
+)
+
+var (
+ errEmptyPayload = errors.New("Empty payload is not a valid metadata level for this operation")
+ errNilPreviousResult = errors.New("The previous results page is nil")
+ errNilNextLink = errors.New("There are no more pages in this query results")
+)
+
+// Entity represents an entity inside an Azure table.
+type Entity struct {
+ Table *Table
+ PartitionKey string
+ RowKey string
+ TimeStamp time.Time
+ OdataMetadata string
+ OdataType string
+ OdataID string
+ OdataEtag string
+ OdataEditLink string
+ Properties map[string]interface{}
+}
+
+// GetEntityReference returns an Entity object with the specified
+// partition key and row key.
+func (t *Table) GetEntityReference(partitionKey, rowKey string) *Entity {
+ return &Entity{
+ PartitionKey: partitionKey,
+ RowKey: rowKey,
+ Table: t,
+ }
+}
+
+// EntityOptions includes options for entity operations.
+type EntityOptions struct {
+ Timeout uint
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// GetEntityOptions includes options for a get entity operation
+type GetEntityOptions struct {
+ Select []string
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// Get gets the referenced entity. Which properties to get can be
+// specified using the select option.
+// See:
+// https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/query-entities
+// https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/querying-tables-and-entities
+func (e *Entity) Get(timeout uint, ml MetadataLevel, options *GetEntityOptions) error {
+ if ml == EmptyPayload {
+ return errEmptyPayload
+ }
+ // RowKey and PartitionKey could be lost if not included in the query
+ // As those are the entity identifiers, it is best if they are not lost
+ rk := e.RowKey
+ pk := e.PartitionKey
+
+ query := url.Values{
+ "timeout": {strconv.FormatUint(uint64(timeout), 10)},
+ }
+ headers := e.Table.tsc.client.getStandardHeaders()
+ headers[headerAccept] = string(ml)
+
+ if options != nil {
+ if len(options.Select) > 0 {
+ query.Add("$select", strings.Join(options.Select, ","))
+ }
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+
+ uri := e.Table.tsc.client.getEndpoint(tableServiceName, e.buildPath(), query)
+ resp, err := e.Table.tsc.client.exec(http.MethodGet, uri, headers, nil, e.Table.tsc.auth)
+ if err != nil {
+ return err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
+ return err
+ }
+
+ respBody, err := ioutil.ReadAll(resp.body)
+ if err != nil {
+ return err
+ }
+ err = json.Unmarshal(respBody, e)
+ if err != nil {
+ return err
+ }
+ e.PartitionKey = pk
+ e.RowKey = rk
+
+ return nil
+}
+
+// Insert inserts the referenced entity in its table.
+// The function fails if there is an entity with the same
+// PartitionKey and RowKey in the table.
+// ml determines the level of detail of metadata in the operation response,
+// or no data at all.
+// See: https://docs.microsoft.com/rest/api/storageservices/fileservices/insert-entity
+func (e *Entity) Insert(ml MetadataLevel, options *EntityOptions) error {
+ query, headers := options.getParameters()
+ headers = mergeHeaders(headers, e.Table.tsc.client.getStandardHeaders())
+
+ body, err := json.Marshal(e)
+ if err != nil {
+ return err
+ }
+ headers = addBodyRelatedHeaders(headers, len(body))
+ headers = addReturnContentHeaders(headers, ml)
+
+ uri := e.Table.tsc.client.getEndpoint(tableServiceName, e.Table.buildPath(), query)
+ resp, err := e.Table.tsc.client.exec(http.MethodPost, uri, headers, bytes.NewReader(body), e.Table.tsc.auth)
+ if err != nil {
+ return err
+ }
+ defer resp.body.Close()
+
+ data, err := ioutil.ReadAll(resp.body)
+ if err != nil {
+ return err
+ }
+
+ if ml != EmptyPayload {
+ if err = checkRespCode(resp.statusCode, []int{http.StatusCreated}); err != nil {
+ return err
+ }
+ if err = e.UnmarshalJSON(data); err != nil {
+ return err
+ }
+ } else {
+ if err = checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// Update updates the contents of an entity. The function fails if there is no entity
+// with the same PartitionKey and RowKey in the table or if the ETag is different
+// than the one in Azure.
+// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/update-entity2
+func (e *Entity) Update(force bool, options *EntityOptions) error {
+ return e.updateMerge(force, http.MethodPut, options)
+}
+
+// Merge merges the contents of entity specified with PartitionKey and RowKey
+// with the content specified in Properties.
+// The function fails if there is no entity with the same PartitionKey and
+// RowKey in the table or if the ETag is different than the one in Azure.
+// Read more: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/merge-entity
+func (e *Entity) Merge(force bool, options *EntityOptions) error {
+ return e.updateMerge(force, "MERGE", options)
+}
+
+// Delete deletes the entity.
+// The function fails if there is no entity with the same PartitionKey and
+// RowKey in the table or if the ETag is different than the one in Azure.
+// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/delete-entity1
+func (e *Entity) Delete(force bool, options *EntityOptions) error {
+ query, headers := options.getParameters()
+ headers = mergeHeaders(headers, e.Table.tsc.client.getStandardHeaders())
+
+ headers = addIfMatchHeader(headers, force, e.OdataEtag)
+ headers = addReturnContentHeaders(headers, EmptyPayload)
+
+ uri := e.Table.tsc.client.getEndpoint(tableServiceName, e.buildPath(), query)
+ resp, err := e.Table.tsc.client.exec(http.MethodDelete, uri, headers, nil, e.Table.tsc.auth)
+ if err != nil {
+ if resp.statusCode == http.StatusPreconditionFailed {
+ return fmt.Errorf(etagErrorTemplate, err)
+ }
+ return err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err = checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil {
+ return err
+ }
+
+ return e.updateTimestamp(resp.headers)
+}
+
+// InsertOrReplace inserts an entity or replaces the existing one.
+// Read more: https://docs.microsoft.com/rest/api/storageservices/fileservices/insert-or-replace-entity
+func (e *Entity) InsertOrReplace(options *EntityOptions) error {
+ return e.insertOr(http.MethodPut, options)
+}
+
+// InsertOrMerge inserts an entity or merges the existing one.
+// Read more: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/insert-or-merge-entity
+func (e *Entity) InsertOrMerge(options *EntityOptions) error {
+ return e.insertOr("MERGE", options)
+}
+
+func (e *Entity) buildPath() string {
+ return fmt.Sprintf("%s(PartitionKey='%s', RowKey='%s')", e.Table.buildPath(), e.PartitionKey, e.RowKey)
+}
+
+// MarshalJSON is a custom marshaller for entity
+func (e *Entity) MarshalJSON() ([]byte, error) {
+ completeMap := map[string]interface{}{}
+ completeMap[partitionKeyNode] = e.PartitionKey
+ completeMap[rowKeyNode] = e.RowKey
+ for k, v := range e.Properties {
+ typeKey := strings.Join([]string{k, OdataTypeSuffix}, "")
+ switch t := v.(type) {
+ case []byte:
+ completeMap[typeKey] = OdataBinary
+ completeMap[k] = string(t)
+ case time.Time:
+ completeMap[typeKey] = OdataDateTime
+ completeMap[k] = t.Format(time.RFC3339Nano)
+ case uuid.UUID:
+ completeMap[typeKey] = OdataGUID
+ completeMap[k] = t.String()
+ case int64:
+ completeMap[typeKey] = OdataInt64
+ completeMap[k] = fmt.Sprintf("%v", v)
+ default:
+ completeMap[k] = v
+ }
+ if strings.HasSuffix(k, OdataTypeSuffix) {
+ if !(completeMap[k] == OdataBinary ||
+ completeMap[k] == OdataDateTime ||
+ completeMap[k] == OdataGUID ||
+ completeMap[k] == OdataInt64) {
+ return nil, fmt.Errorf("Odata.type annotation %v value is not valid", k)
+ }
+ valueKey := strings.TrimSuffix(k, OdataTypeSuffix)
+ if _, ok := completeMap[valueKey]; !ok {
+ return nil, fmt.Errorf("Odata.type annotation %v defined without value defined", k)
+ }
+ }
+ }
+ return json.Marshal(completeMap)
+}
+
+// UnmarshalJSON is a custom unmarshaller for entities
+func (e *Entity) UnmarshalJSON(data []byte) error {
+ errorTemplate := "Deserializing error: %v"
+
+ props := map[string]interface{}{}
+ err := json.Unmarshal(data, &props)
+ if err != nil {
+ return err
+ }
+
+ // deselialize metadata
+ e.OdataMetadata = stringFromMap(props, "odata.metadata")
+ e.OdataType = stringFromMap(props, "odata.type")
+ e.OdataID = stringFromMap(props, "odata.id")
+ e.OdataEtag = stringFromMap(props, "odata.etag")
+ e.OdataEditLink = stringFromMap(props, "odata.editLink")
+ e.PartitionKey = stringFromMap(props, partitionKeyNode)
+ e.RowKey = stringFromMap(props, rowKeyNode)
+
+ // deserialize timestamp
+ timeStamp, ok := props["Timestamp"]
+ if ok {
+ str, ok := timeStamp.(string)
+ if !ok {
+ return fmt.Errorf(errorTemplate, "Timestamp casting error")
+ }
+ t, err := time.Parse(time.RFC3339Nano, str)
+ if err != nil {
+ return fmt.Errorf(errorTemplate, err)
+ }
+ e.TimeStamp = t
+ }
+ delete(props, "Timestamp")
+ delete(props, "Timestamp@odata.type")
+
+ // deserialize entity (user defined fields)
+ for k, v := range props {
+ if strings.HasSuffix(k, OdataTypeSuffix) {
+ valueKey := strings.TrimSuffix(k, OdataTypeSuffix)
+ str, ok := props[valueKey].(string)
+ if !ok {
+ return fmt.Errorf(errorTemplate, fmt.Sprintf("%v casting error", v))
+ }
+ switch v {
+ case OdataBinary:
+ props[valueKey] = []byte(str)
+ case OdataDateTime:
+ t, err := time.Parse("2006-01-02T15:04:05Z", str)
+ if err != nil {
+ return fmt.Errorf(errorTemplate, err)
+ }
+ props[valueKey] = t
+ case OdataGUID:
+ props[valueKey] = uuid.FromStringOrNil(str)
+ case OdataInt64:
+ i, err := strconv.ParseInt(str, 10, 64)
+ if err != nil {
+ return fmt.Errorf(errorTemplate, err)
+ }
+ props[valueKey] = i
+ default:
+ return fmt.Errorf(errorTemplate, fmt.Sprintf("%v is not supported", v))
+ }
+ delete(props, k)
+ }
+ }
+
+ e.Properties = props
+ return nil
+}
+
+func getAndDelete(props map[string]interface{}, key string) interface{} {
+ if value, ok := props[key]; ok {
+ delete(props, key)
+ return value
+ }
+ return nil
+}
+
+func addIfMatchHeader(h map[string]string, force bool, etag string) map[string]string {
+ if force {
+ h[headerIfMatch] = "*"
+ } else {
+ h[headerIfMatch] = etag
+ }
+ return h
+}
+
+// updates Etag and timestamp
+func (e *Entity) updateEtagAndTimestamp(headers http.Header) error {
+ e.OdataEtag = headers.Get(headerEtag)
+ return e.updateTimestamp(headers)
+}
+
+func (e *Entity) updateTimestamp(headers http.Header) error {
+ str := headers.Get(headerDate)
+ t, err := time.Parse(time.RFC1123, str)
+ if err != nil {
+ return fmt.Errorf("Update timestamp error: %v", err)
+ }
+ e.TimeStamp = t
+ return nil
+}
+
+func (e *Entity) insertOr(verb string, options *EntityOptions) error {
+ query, headers := options.getParameters()
+ headers = mergeHeaders(headers, e.Table.tsc.client.getStandardHeaders())
+
+ body, err := json.Marshal(e)
+ if err != nil {
+ return err
+ }
+ headers = addBodyRelatedHeaders(headers, len(body))
+ headers = addReturnContentHeaders(headers, EmptyPayload)
+
+ uri := e.Table.tsc.client.getEndpoint(tableServiceName, e.buildPath(), query)
+ resp, err := e.Table.tsc.client.exec(verb, uri, headers, bytes.NewReader(body), e.Table.tsc.auth)
+ if err != nil {
+ return err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err = checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil {
+ return err
+ }
+
+ return e.updateEtagAndTimestamp(resp.headers)
+}
+
+func (e *Entity) updateMerge(force bool, verb string, options *EntityOptions) error {
+ query, headers := options.getParameters()
+ headers = mergeHeaders(headers, e.Table.tsc.client.getStandardHeaders())
+
+ body, err := json.Marshal(e)
+ if err != nil {
+ return err
+ }
+ headers = addBodyRelatedHeaders(headers, len(body))
+ headers = addIfMatchHeader(headers, force, e.OdataEtag)
+ headers = addReturnContentHeaders(headers, EmptyPayload)
+
+ uri := e.Table.tsc.client.getEndpoint(tableServiceName, e.buildPath(), query)
+ resp, err := e.Table.tsc.client.exec(verb, uri, headers, bytes.NewReader(body), e.Table.tsc.auth)
+ if err != nil {
+ if resp.statusCode == http.StatusPreconditionFailed {
+ return fmt.Errorf(etagErrorTemplate, err)
+ }
+ return err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err = checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil {
+ return err
+ }
+
+ return e.updateEtagAndTimestamp(resp.headers)
+}
+
+func stringFromMap(props map[string]interface{}, key string) string {
+ value := getAndDelete(props, key)
+ if value != nil {
+ return value.(string)
+ }
+ return ""
+}
+
+func (options *EntityOptions) getParameters() (url.Values, map[string]string) {
+ query := url.Values{}
+ headers := map[string]string{}
+ if options != nil {
+ query = addTimeout(query, options.Timeout)
+ headers = headersFromStruct(*options)
+ }
+ return query, headers
+}
diff --git a/vendor/github.com/Azure/azure-storage-go/file.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/file.go
similarity index 64%
rename from vendor/github.com/Azure/azure-storage-go/file.go
rename to vendor/github.com/Azure/azure-sdk-for-go/storage/file.go
index e4901a114..238ac6d6d 100644
--- a/vendor/github.com/Azure/azure-storage-go/file.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/file.go
@@ -32,7 +32,7 @@ type FileProperties struct {
Etag string
Language string `header:"x-ms-content-language"`
LastModified string
- Length uint64 `xml:"Content-Length"`
+ Length uint64 `xml:"Content-Length" header:"x-ms-content-length"`
MD5 string `header:"x-ms-content-md5"`
Type string `header:"x-ms-content-type"`
}
@@ -54,26 +54,22 @@ type FileStream struct {
}
// FileRequestOptions will be passed to misc file operations.
-// Currently just Timeout (in seconds) but will expand.
+// Currently just Timeout (in seconds) but could expand.
type FileRequestOptions struct {
Timeout uint // timeout duration in seconds.
}
-// getParameters, construct parameters for FileRequestOptions.
-// currently only timeout, but expecting to grow as functionality fills out.
-func (p FileRequestOptions) getParameters() url.Values {
- out := url.Values{}
-
- if p.Timeout != 0 {
- out.Set("timeout", fmt.Sprintf("%v", p.Timeout))
+func prepareOptions(options *FileRequestOptions) url.Values {
+ params := url.Values{}
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
}
-
- return out
+ return params
}
// FileRanges contains a list of file range information for a file.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn166984.aspx
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/List-Ranges
type FileRanges struct {
ContentLength uint64
LastModified string
@@ -83,7 +79,7 @@ type FileRanges struct {
// FileRange contains range information for a file.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn166984.aspx
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/List-Ranges
type FileRange struct {
Start uint64 `xml:"Start"`
End uint64 `xml:"End"`
@@ -100,9 +96,13 @@ func (f *File) buildPath() string {
// ClearRange releases the specified range of space in a file.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn194276.aspx
-func (f *File) ClearRange(fileRange FileRange) error {
- headers, err := f.modifyRange(nil, fileRange, nil)
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Range
+func (f *File) ClearRange(fileRange FileRange, options *FileRequestOptions) error {
+ var timeout *uint
+ if options != nil {
+ timeout = &options.Timeout
+ }
+ headers, err := f.modifyRange(nil, fileRange, timeout, nil)
if err != nil {
return err
}
@@ -113,24 +113,23 @@ func (f *File) ClearRange(fileRange FileRange) error {
// Create creates a new file or replaces an existing one.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn194271.aspx
-func (f *File) Create(maxSize uint64) error {
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Create-File
+func (f *File) Create(maxSize uint64, options *FileRequestOptions) error {
if maxSize > oneTB {
return fmt.Errorf("max file size is 1TB")
}
+ params := prepareOptions(options)
+ headers := headersFromStruct(f.Properties)
+ headers["x-ms-content-length"] = strconv.FormatUint(maxSize, 10)
+ headers["x-ms-type"] = "file"
- extraHeaders := map[string]string{
- "x-ms-content-length": strconv.FormatUint(maxSize, 10),
- "x-ms-type": "file",
- }
-
- headers, err := f.fsc.createResource(f.buildPath(), resourceFile, nil, mergeMDIntoExtraHeaders(f.Metadata, extraHeaders), []int{http.StatusCreated})
+ outputHeaders, err := f.fsc.createResource(f.buildPath(), resourceFile, params, mergeMDIntoExtraHeaders(f.Metadata, headers), []int{http.StatusCreated})
if err != nil {
return err
}
f.Properties.Length = maxSize
- f.updateEtagAndLastModified(headers)
+ f.updateEtagAndLastModified(outputHeaders)
return nil
}
@@ -142,13 +141,9 @@ func (f *File) CopyFile(sourceURL string, options *FileRequestOptions) error {
"x-ms-type": "file",
"x-ms-copy-source": sourceURL,
}
+ params := prepareOptions(options)
- var parameters url.Values
- if options != nil {
- parameters = options.getParameters()
- }
-
- headers, err := f.fsc.createResource(f.buildPath(), resourceFile, parameters, mergeMDIntoExtraHeaders(f.Metadata, extraHeaders), []int{http.StatusAccepted})
+ headers, err := f.fsc.createResource(f.buildPath(), resourceFile, params, mergeMDIntoExtraHeaders(f.Metadata, extraHeaders), []int{http.StatusAccepted})
if err != nil {
return err
}
@@ -159,16 +154,16 @@ func (f *File) CopyFile(sourceURL string, options *FileRequestOptions) error {
// Delete immediately removes this file from the storage account.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn689085.aspx
-func (f *File) Delete() error {
- return f.fsc.deleteResource(f.buildPath(), resourceFile)
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Delete-File2
+func (f *File) Delete(options *FileRequestOptions) error {
+ return f.fsc.deleteResource(f.buildPath(), resourceFile, options)
}
// DeleteIfExists removes this file if it exists.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn689085.aspx
-func (f *File) DeleteIfExists() (bool, error) {
- resp, err := f.fsc.deleteResourceNoClose(f.buildPath(), resourceFile)
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Delete-File2
+func (f *File) DeleteIfExists(options *FileRequestOptions) (bool, error) {
+ resp, err := f.fsc.deleteResourceNoClose(f.buildPath(), resourceFile, options)
if resp != nil {
defer readAndCloseBody(resp.body)
if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound {
@@ -178,33 +173,59 @@ func (f *File) DeleteIfExists() (bool, error) {
return false, err
}
+// GetFileOptions includes options for a get file operation
+type GetFileOptions struct {
+ Timeout uint
+ GetContentMD5 bool
+}
+
+// DownloadToStream operation downloads the file.
+//
+// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-file
+func (f *File) DownloadToStream(options *FileRequestOptions) (io.ReadCloser, error) {
+ params := prepareOptions(options)
+ resp, err := f.fsc.getResourceNoClose(f.buildPath(), compNone, resourceFile, params, http.MethodGet, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
+ readAndCloseBody(resp.body)
+ return nil, err
+ }
+ return resp.body, nil
+}
+
// DownloadRangeToStream operation downloads the specified range of this file with optional MD5 hash.
//
// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-file
-func (f *File) DownloadRangeToStream(fileRange FileRange, getContentMD5 bool) (fs FileStream, err error) {
- if getContentMD5 && isRangeTooBig(fileRange) {
- return fs, fmt.Errorf("must specify a range less than or equal to 4MB when getContentMD5 is true")
- }
-
+func (f *File) DownloadRangeToStream(fileRange FileRange, options *GetFileOptions) (fs FileStream, err error) {
extraHeaders := map[string]string{
"Range": fileRange.String(),
}
- if getContentMD5 == true {
- extraHeaders["x-ms-range-get-content-md5"] = "true"
+ params := url.Values{}
+ if options != nil {
+ if options.GetContentMD5 {
+ if isRangeTooBig(fileRange) {
+ return fs, fmt.Errorf("must specify a range less than or equal to 4MB when getContentMD5 is true")
+ }
+ extraHeaders["x-ms-range-get-content-md5"] = "true"
+ }
+ params = addTimeout(params, options.Timeout)
}
- resp, err := f.fsc.getResourceNoClose(f.buildPath(), compNone, resourceFile, http.MethodGet, extraHeaders)
+ resp, err := f.fsc.getResourceNoClose(f.buildPath(), compNone, resourceFile, params, http.MethodGet, extraHeaders)
if err != nil {
return fs, err
}
if err = checkRespCode(resp.statusCode, []int{http.StatusOK, http.StatusPartialContent}); err != nil {
- resp.body.Close()
+ readAndCloseBody(resp.body)
return fs, err
}
fs.Body = resp.body
- if getContentMD5 {
+ if options != nil && options.GetContentMD5 {
fs.ContentMD5 = resp.headers.Get("Content-MD5")
}
return fs, nil
@@ -221,8 +242,10 @@ func (f *File) Exists() (bool, error) {
}
// FetchAttributes updates metadata and properties for this file.
-func (f *File) FetchAttributes() error {
- headers, err := f.fsc.getResourceHeaders(f.buildPath(), compNone, resourceFile, http.MethodHead)
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-file-properties
+func (f *File) FetchAttributes(options *FileRequestOptions) error {
+ params := prepareOptions(options)
+ headers, err := f.fsc.getResourceHeaders(f.buildPath(), compNone, resourceFile, params, http.MethodHead)
if err != nil {
return err
}
@@ -242,17 +265,26 @@ func isRangeTooBig(fileRange FileRange) bool {
return false
}
+// ListRangesOptions includes options for a list file ranges operation
+type ListRangesOptions struct {
+ Timeout uint
+ ListRange *FileRange
+}
+
// ListRanges returns the list of valid ranges for this file.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn166984.aspx
-func (f *File) ListRanges(listRange *FileRange) (*FileRanges, error) {
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/List-Ranges
+func (f *File) ListRanges(options *ListRangesOptions) (*FileRanges, error) {
params := url.Values{"comp": {"rangelist"}}
// add optional range to list
var headers map[string]string
- if listRange != nil {
- headers = make(map[string]string)
- headers["Range"] = listRange.String()
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ if options.ListRange != nil {
+ headers = make(map[string]string)
+ headers["Range"] = options.ListRange.String()
+ }
}
resp, err := f.fsc.listContent(f.buildPath(), params, headers)
@@ -278,7 +310,7 @@ func (f *File) ListRanges(listRange *FileRange) (*FileRanges, error) {
}
// modifies a range of bytes in this file
-func (f *File) modifyRange(bytes io.Reader, fileRange FileRange, contentMD5 *string) (http.Header, error) {
+func (f *File) modifyRange(bytes io.Reader, fileRange FileRange, timeout *uint, contentMD5 *string) (http.Header, error) {
if err := f.fsc.checkForStorageEmulator(); err != nil {
return nil, err
}
@@ -289,7 +321,12 @@ func (f *File) modifyRange(bytes io.Reader, fileRange FileRange, contentMD5 *str
return nil, errors.New("range cannot exceed 4MB in size")
}
- uri := f.fsc.client.getEndpoint(fileServiceName, f.buildPath(), url.Values{"comp": {"range"}})
+ params := url.Values{"comp": {"range"}}
+ if timeout != nil {
+ params = addTimeout(params, *timeout)
+ }
+
+ uri := f.fsc.client.getEndpoint(fileServiceName, f.buildPath(), params)
// default to clear
write := "clear"
@@ -327,9 +364,9 @@ func (f *File) modifyRange(bytes io.Reader, fileRange FileRange, contentMD5 *str
// are case-insensitive so case munging should not matter to other
// applications either.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn689097.aspx
-func (f *File) SetMetadata() error {
- headers, err := f.fsc.setResourceHeaders(f.buildPath(), compMetadata, resourceFile, mergeMDIntoExtraHeaders(f.Metadata, nil))
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Set-File-Metadata
+func (f *File) SetMetadata(options *FileRequestOptions) error {
+ headers, err := f.fsc.setResourceHeaders(f.buildPath(), compMetadata, resourceFile, mergeMDIntoExtraHeaders(f.Metadata, nil), options)
if err != nil {
return err
}
@@ -345,9 +382,9 @@ func (f *File) SetMetadata() error {
// are case-insensitive so case munging should not matter to other
// applications either.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn166975.aspx
-func (f *File) SetProperties() error {
- headers, err := f.fsc.setResourceHeaders(f.buildPath(), compProperties, resourceFile, headersFromStruct(f.Properties))
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Set-File-Properties
+func (f *File) SetProperties(options *FileRequestOptions) error {
+ headers, err := f.fsc.setResourceHeaders(f.buildPath(), compProperties, resourceFile, headersFromStruct(f.Properties), options)
if err != nil {
return err
}
@@ -390,19 +427,32 @@ func (f *File) updateProperties(header http.Header) {
// This method does not create a publicly accessible URL if the file
// is private and this method does not check if the file exists.
func (f *File) URL() string {
- return f.fsc.client.getEndpoint(fileServiceName, f.buildPath(), url.Values{})
+ return f.fsc.client.getEndpoint(fileServiceName, f.buildPath(), nil)
}
-// WriteRange writes a range of bytes to this file with an optional MD5 hash of the content.
-// Note that the length of bytes must match (rangeEnd - rangeStart) + 1 with a maximum size of 4MB.
+// WriteRangeOptions includes opptions for a write file range operation
+type WriteRangeOptions struct {
+ Timeout uint
+ ContentMD5 string
+}
+
+// WriteRange writes a range of bytes to this file with an optional MD5 hash of the content (inside
+// options parameter). Note that the length of bytes must match (rangeEnd - rangeStart) + 1 with
+// a maximum size of 4MB.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn194276.aspx
-func (f *File) WriteRange(bytes io.Reader, fileRange FileRange, contentMD5 *string) error {
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Range
+func (f *File) WriteRange(bytes io.Reader, fileRange FileRange, options *WriteRangeOptions) error {
if bytes == nil {
return errors.New("bytes cannot be nil")
}
+ var timeout *uint
+ var md5 *string
+ if options != nil {
+ timeout = &options.Timeout
+ md5 = &options.ContentMD5
+ }
- headers, err := f.modifyRange(bytes, fileRange, contentMD5)
+ headers, err := f.modifyRange(bytes, fileRange, timeout, md5)
if err != nil {
return err
}
diff --git a/vendor/github.com/Azure/azure-storage-go/fileserviceclient.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/fileserviceclient.go
similarity index 76%
rename from vendor/github.com/Azure/azure-storage-go/fileserviceclient.go
rename to vendor/github.com/Azure/azure-sdk-for-go/storage/fileserviceclient.go
index d68bd7f64..81217bdfa 100644
--- a/vendor/github.com/Azure/azure-storage-go/fileserviceclient.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/fileserviceclient.go
@@ -5,7 +5,7 @@ import (
"fmt"
"net/http"
"net/url"
- "strings"
+ "strconv"
)
// FileServiceClient contains operations for Microsoft Azure File Service.
@@ -17,7 +17,7 @@ type FileServiceClient struct {
// ListSharesParameters defines the set of customizable parameters to make a
// List Shares call.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn167009.aspx
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/List-Shares
type ListSharesParameters struct {
Prefix string
Marker string
@@ -29,7 +29,7 @@ type ListSharesParameters struct {
// ShareListResponse contains the response fields from
// ListShares call.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn167009.aspx
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/List-Shares
type ShareListResponse struct {
XMLName xml.Name `xml:"EnumerationResults"`
Xmlns string `xml:"xmlns,attr"`
@@ -79,10 +79,10 @@ func (p ListSharesParameters) getParameters() url.Values {
out.Set("include", p.Include)
}
if p.MaxResults != 0 {
- out.Set("maxresults", fmt.Sprintf("%v", p.MaxResults))
+ out.Set("maxresults", strconv.FormatUint(uint64(p.MaxResults), 10))
}
if p.Timeout != 0 {
- out.Set("timeout", fmt.Sprintf("%v", p.Timeout))
+ out.Set("timeout", strconv.FormatUint(uint64(p.Timeout), 10))
}
return out
@@ -91,15 +91,16 @@ func (p ListSharesParameters) getParameters() url.Values {
func (p ListDirsAndFilesParameters) getParameters() url.Values {
out := url.Values{}
+ if p.Prefix != "" {
+ out.Set("prefix", p.Prefix)
+ }
if p.Marker != "" {
out.Set("marker", p.Marker)
}
if p.MaxResults != 0 {
- out.Set("maxresults", fmt.Sprintf("%v", p.MaxResults))
- }
- if p.Timeout != 0 {
- out.Set("timeout", fmt.Sprintf("%v", p.Timeout))
+ out.Set("maxresults", strconv.FormatUint(uint64(p.MaxResults), 10))
}
+ out = addTimeout(out, p.Timeout)
return out
}
@@ -117,9 +118,9 @@ func getURLInitValues(comp compType, res resourceType) url.Values {
}
// GetShareReference returns a Share object for the specified share name.
-func (f FileServiceClient) GetShareReference(name string) Share {
- return Share{
- fsc: &f,
+func (f *FileServiceClient) GetShareReference(name string) *Share {
+ return &Share{
+ fsc: f,
Name: name,
Properties: ShareProperties{
Quota: -1,
@@ -130,7 +131,7 @@ func (f FileServiceClient) GetShareReference(name string) Share {
// ListShares returns the list of shares in a storage account along with
// pagination token and other response details.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/list-shares
func (f FileServiceClient) ListShares(params ListSharesParameters) (*ShareListResponse, error) {
q := mergeParams(params.getParameters(), url.Values{"comp": {"list"}})
@@ -231,8 +232,8 @@ func (f FileServiceClient) createResourceNoClose(path string, res resourceType,
}
// returns HTTP header data for the specified directory or share
-func (f FileServiceClient) getResourceHeaders(path string, comp compType, res resourceType, verb string) (http.Header, error) {
- resp, err := f.getResourceNoClose(path, comp, res, verb, nil)
+func (f FileServiceClient) getResourceHeaders(path string, comp compType, res resourceType, params url.Values, verb string) (http.Header, error) {
+ resp, err := f.getResourceNoClose(path, comp, res, params, verb, nil)
if err != nil {
return nil, err
}
@@ -246,22 +247,21 @@ func (f FileServiceClient) getResourceHeaders(path string, comp compType, res re
}
// gets the specified resource, doesn't close the response body
-func (f FileServiceClient) getResourceNoClose(path string, comp compType, res resourceType, verb string, extraHeaders map[string]string) (*storageResponse, error) {
+func (f FileServiceClient) getResourceNoClose(path string, comp compType, res resourceType, params url.Values, verb string, extraHeaders map[string]string) (*storageResponse, error) {
if err := f.checkForStorageEmulator(); err != nil {
return nil, err
}
- params := getURLInitValues(comp, res)
+ params = mergeParams(params, getURLInitValues(comp, res))
uri := f.client.getEndpoint(fileServiceName, path, params)
- extraHeaders = f.client.protectUserAgent(extraHeaders)
headers := mergeHeaders(f.client.getStandardHeaders(), extraHeaders)
return f.client.exec(verb, uri, headers, nil, f.auth)
}
// deletes the resource and returns the response
-func (f FileServiceClient) deleteResource(path string, res resourceType) error {
- resp, err := f.deleteResourceNoClose(path, res)
+func (f FileServiceClient) deleteResource(path string, res resourceType, options *FileRequestOptions) error {
+ resp, err := f.deleteResourceNoClose(path, res, options)
if err != nil {
return err
}
@@ -270,12 +270,12 @@ func (f FileServiceClient) deleteResource(path string, res resourceType) error {
}
// deletes the resource and returns the response, doesn't close the response body
-func (f FileServiceClient) deleteResourceNoClose(path string, res resourceType) (*storageResponse, error) {
+func (f FileServiceClient) deleteResourceNoClose(path string, res resourceType, options *FileRequestOptions) (*storageResponse, error) {
if err := f.checkForStorageEmulator(); err != nil {
return nil, err
}
- values := getURLInitValues(compNone, res)
+ values := mergeParams(getURLInitValues(compNone, res), prepareOptions(options))
uri := f.client.getEndpoint(fileServiceName, path, values)
return f.client.exec(http.MethodDelete, uri, f.client.getStandardHeaders(), nil, f.auth)
}
@@ -294,21 +294,13 @@ func mergeMDIntoExtraHeaders(metadata, extraHeaders map[string]string) map[strin
return extraHeaders
}
-// merges extraHeaders into headers and returns headers
-func mergeHeaders(headers, extraHeaders map[string]string) map[string]string {
- for k, v := range extraHeaders {
- headers[k] = v
- }
- return headers
-}
-
// sets extra header data for the specified resource
-func (f FileServiceClient) setResourceHeaders(path string, comp compType, res resourceType, extraHeaders map[string]string) (http.Header, error) {
+func (f FileServiceClient) setResourceHeaders(path string, comp compType, res resourceType, extraHeaders map[string]string, options *FileRequestOptions) (http.Header, error) {
if err := f.checkForStorageEmulator(); err != nil {
return nil, err
}
- params := getURLInitValues(comp, res)
+ params := mergeParams(getURLInitValues(comp, res), prepareOptions(options))
uri := f.client.getEndpoint(fileServiceName, path, params)
extraHeaders = f.client.protectUserAgent(extraHeaders)
headers := mergeHeaders(f.client.getStandardHeaders(), extraHeaders)
@@ -322,49 +314,6 @@ func (f FileServiceClient) setResourceHeaders(path string, comp compType, res re
return resp.headers, checkRespCode(resp.statusCode, []int{http.StatusOK})
}
-// gets metadata for the specified resource
-func (f FileServiceClient) getMetadata(path string, res resourceType) (map[string]string, error) {
- if err := f.checkForStorageEmulator(); err != nil {
- return nil, err
- }
-
- headers, err := f.getResourceHeaders(path, compMetadata, res, http.MethodGet)
- if err != nil {
- return nil, err
- }
-
- return getMetadataFromHeaders(headers), nil
-}
-
-// returns a map of custom metadata values from the specified HTTP header
-func getMetadataFromHeaders(header http.Header) map[string]string {
- metadata := make(map[string]string)
- for k, v := range header {
- // Can't trust CanonicalHeaderKey() to munge case
- // reliably. "_" is allowed in identifiers:
- // https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx
- // https://msdn.microsoft.com/library/aa664670(VS.71).aspx
- // http://tools.ietf.org/html/rfc7230#section-3.2
- // ...but "_" is considered invalid by
- // CanonicalMIMEHeaderKey in
- // https://golang.org/src/net/textproto/reader.go?s=14615:14659#L542
- // so k can be "X-Ms-Meta-Foo" or "x-ms-meta-foo_bar".
- k = strings.ToLower(k)
- if len(v) == 0 || !strings.HasPrefix(k, strings.ToLower(userDefinedMetadataHeaderPrefix)) {
- continue
- }
- // metadata["foo"] = content of the last X-Ms-Meta-Foo header
- k = k[len(userDefinedMetadataHeaderPrefix):]
- metadata[k] = v[len(v)-1]
- }
-
- if len(metadata) == 0 {
- return nil
- }
-
- return metadata
-}
-
//checkForStorageEmulator determines if the client is setup for use with
//Azure Storage Emulator, and returns a relevant error
func (f FileServiceClient) checkForStorageEmulator() error {
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/leaseblob.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/leaseblob.go
new file mode 100644
index 000000000..415b74018
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/leaseblob.go
@@ -0,0 +1,187 @@
+package storage
+
+import (
+ "errors"
+ "net/http"
+ "net/url"
+ "strconv"
+ "time"
+)
+
+// lease constants.
+const (
+ leaseHeaderPrefix = "x-ms-lease-"
+ headerLeaseID = "x-ms-lease-id"
+ leaseAction = "x-ms-lease-action"
+ leaseBreakPeriod = "x-ms-lease-break-period"
+ leaseDuration = "x-ms-lease-duration"
+ leaseProposedID = "x-ms-proposed-lease-id"
+ leaseTime = "x-ms-lease-time"
+
+ acquireLease = "acquire"
+ renewLease = "renew"
+ changeLease = "change"
+ releaseLease = "release"
+ breakLease = "break"
+)
+
+// leasePut is common PUT code for the various acquire/release/break etc functions.
+func (b *Blob) leaseCommonPut(headers map[string]string, expectedStatus int, options *LeaseOptions) (http.Header, error) {
+ params := url.Values{"comp": {"lease"}}
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
+ if err != nil {
+ return nil, err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err := checkRespCode(resp.statusCode, []int{expectedStatus}); err != nil {
+ return nil, err
+ }
+
+ return resp.headers, nil
+}
+
+// LeaseOptions includes options for all operations regarding leasing blobs
+type LeaseOptions struct {
+ Timeout uint
+ Origin string `header:"Origin"`
+ IfMatch string `header:"If-Match"`
+ IfNoneMatch string `header:"If-None-Match"`
+ IfModifiedSince *time.Time `header:"If-Modified-Since"`
+ IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// AcquireLease creates a lease for a blob
+// returns leaseID acquired
+// In API Versions starting on 2012-02-12, the minimum leaseTimeInSeconds is 15, the maximum
+// non-infinite leaseTimeInSeconds is 60. To specify an infinite lease, provide the value -1.
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Lease-Blob
+func (b *Blob) AcquireLease(leaseTimeInSeconds int, proposedLeaseID string, options *LeaseOptions) (returnedLeaseID string, err error) {
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers[leaseAction] = acquireLease
+
+ if leaseTimeInSeconds == -1 {
+ // Do nothing, but don't trigger the following clauses.
+ } else if leaseTimeInSeconds > 60 || b.Container.bsc.client.apiVersion < "2012-02-12" {
+ leaseTimeInSeconds = 60
+ } else if leaseTimeInSeconds < 15 {
+ leaseTimeInSeconds = 15
+ }
+
+ headers[leaseDuration] = strconv.Itoa(leaseTimeInSeconds)
+
+ if proposedLeaseID != "" {
+ headers[leaseProposedID] = proposedLeaseID
+ }
+
+ respHeaders, err := b.leaseCommonPut(headers, http.StatusCreated, options)
+ if err != nil {
+ return "", err
+ }
+
+ returnedLeaseID = respHeaders.Get(http.CanonicalHeaderKey(headerLeaseID))
+
+ if returnedLeaseID != "" {
+ return returnedLeaseID, nil
+ }
+
+ return "", errors.New("LeaseID not returned")
+}
+
+// BreakLease breaks the lease for a blob
+// Returns the timeout remaining in the lease in seconds
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Lease-Blob
+func (b *Blob) BreakLease(options *LeaseOptions) (breakTimeout int, err error) {
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers[leaseAction] = breakLease
+ return b.breakLeaseCommon(headers, options)
+}
+
+// BreakLeaseWithBreakPeriod breaks the lease for a blob
+// breakPeriodInSeconds is used to determine how long until new lease can be created.
+// Returns the timeout remaining in the lease in seconds
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Lease-Blob
+func (b *Blob) BreakLeaseWithBreakPeriod(breakPeriodInSeconds int, options *LeaseOptions) (breakTimeout int, err error) {
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers[leaseAction] = breakLease
+ headers[leaseBreakPeriod] = strconv.Itoa(breakPeriodInSeconds)
+ return b.breakLeaseCommon(headers, options)
+}
+
+// breakLeaseCommon is common code for both version of BreakLease (with and without break period)
+func (b *Blob) breakLeaseCommon(headers map[string]string, options *LeaseOptions) (breakTimeout int, err error) {
+
+ respHeaders, err := b.leaseCommonPut(headers, http.StatusAccepted, options)
+ if err != nil {
+ return 0, err
+ }
+
+ breakTimeoutStr := respHeaders.Get(http.CanonicalHeaderKey(leaseTime))
+ if breakTimeoutStr != "" {
+ breakTimeout, err = strconv.Atoi(breakTimeoutStr)
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ return breakTimeout, nil
+}
+
+// ChangeLease changes a lease ID for a blob
+// Returns the new LeaseID acquired
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Lease-Blob
+func (b *Blob) ChangeLease(currentLeaseID string, proposedLeaseID string, options *LeaseOptions) (newLeaseID string, err error) {
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers[leaseAction] = changeLease
+ headers[headerLeaseID] = currentLeaseID
+ headers[leaseProposedID] = proposedLeaseID
+
+ respHeaders, err := b.leaseCommonPut(headers, http.StatusOK, options)
+ if err != nil {
+ return "", err
+ }
+
+ newLeaseID = respHeaders.Get(http.CanonicalHeaderKey(headerLeaseID))
+ if newLeaseID != "" {
+ return newLeaseID, nil
+ }
+
+ return "", errors.New("LeaseID not returned")
+}
+
+// ReleaseLease releases the lease for a blob
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Lease-Blob
+func (b *Blob) ReleaseLease(currentLeaseID string, options *LeaseOptions) error {
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers[leaseAction] = releaseLease
+ headers[headerLeaseID] = currentLeaseID
+
+ _, err := b.leaseCommonPut(headers, http.StatusOK, options)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// RenewLease renews the lease for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
+func (b *Blob) RenewLease(currentLeaseID string, options *LeaseOptions) error {
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers[leaseAction] = renewLease
+ headers[headerLeaseID] = currentLeaseID
+
+ _, err := b.leaseCommonPut(headers, http.StatusOK, options)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/message.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/message.go
new file mode 100644
index 000000000..3ededcd42
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/message.go
@@ -0,0 +1,153 @@
+package storage
+
+import (
+ "encoding/xml"
+ "fmt"
+ "net/http"
+ "net/url"
+ "strconv"
+ "time"
+)
+
+// Message represents an Azure message.
+type Message struct {
+ Queue *Queue
+ Text string `xml:"MessageText"`
+ ID string `xml:"MessageId"`
+ Insertion TimeRFC1123 `xml:"InsertionTime"`
+ Expiration TimeRFC1123 `xml:"ExpirationTime"`
+ PopReceipt string `xml:"PopReceipt"`
+ NextVisible TimeRFC1123 `xml:"TimeNextVisible"`
+ DequeueCount int `xml:"DequeueCount"`
+}
+
+func (m *Message) buildPath() string {
+ return fmt.Sprintf("%s/%s", m.Queue.buildPathMessages(), m.ID)
+}
+
+// PutMessageOptions is the set of options can be specified for Put Messsage
+// operation. A zero struct does not use any preferences for the request.
+type PutMessageOptions struct {
+ Timeout uint
+ VisibilityTimeout int
+ MessageTTL int
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// Put operation adds a new message to the back of the message queue.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Message
+func (m *Message) Put(options *PutMessageOptions) error {
+ query := url.Values{}
+ headers := m.Queue.qsc.client.getStandardHeaders()
+
+ req := putMessageRequest{MessageText: m.Text}
+ body, nn, err := xmlMarshal(req)
+ if err != nil {
+ return err
+ }
+ headers["Content-Length"] = strconv.Itoa(nn)
+
+ if options != nil {
+ if options.VisibilityTimeout != 0 {
+ query.Set("visibilitytimeout", strconv.Itoa(options.VisibilityTimeout))
+ }
+ if options.MessageTTL != 0 {
+ query.Set("messagettl", strconv.Itoa(options.MessageTTL))
+ }
+ query = addTimeout(query, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+
+ uri := m.Queue.qsc.client.getEndpoint(queueServiceName, m.Queue.buildPathMessages(), query)
+ resp, err := m.Queue.qsc.client.exec(http.MethodPost, uri, headers, body, m.Queue.qsc.auth)
+ if err != nil {
+ return err
+ }
+ defer readAndCloseBody(resp.body)
+
+ err = xmlUnmarshal(resp.body, m)
+ if err != nil {
+ return err
+ }
+ return checkRespCode(resp.statusCode, []int{http.StatusCreated})
+}
+
+// UpdateMessageOptions is the set of options can be specified for Update Messsage
+// operation. A zero struct does not use any preferences for the request.
+type UpdateMessageOptions struct {
+ Timeout uint
+ VisibilityTimeout int
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// Update operation updates the specified message.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Update-Message
+func (m *Message) Update(options *UpdateMessageOptions) error {
+ query := url.Values{}
+ if m.PopReceipt != "" {
+ query.Set("popreceipt", m.PopReceipt)
+ }
+
+ headers := m.Queue.qsc.client.getStandardHeaders()
+ req := putMessageRequest{MessageText: m.Text}
+ body, nn, err := xmlMarshal(req)
+ if err != nil {
+ return err
+ }
+ headers["Content-Length"] = strconv.Itoa(nn)
+
+ if options != nil {
+ if options.VisibilityTimeout != 0 {
+ query.Set("visibilitytimeout", strconv.Itoa(options.VisibilityTimeout))
+ }
+ query = addTimeout(query, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := m.Queue.qsc.client.getEndpoint(queueServiceName, m.buildPath(), query)
+
+ resp, err := m.Queue.qsc.client.exec(http.MethodPut, uri, headers, body, m.Queue.qsc.auth)
+ if err != nil {
+ return err
+ }
+ defer readAndCloseBody(resp.body)
+
+ m.PopReceipt = resp.headers.Get("x-ms-popreceipt")
+ nextTimeStr := resp.headers.Get("x-ms-time-next-visible")
+ if nextTimeStr != "" {
+ nextTime, err := time.Parse(time.RFC1123, nextTimeStr)
+ if err != nil {
+ return err
+ }
+ m.NextVisible = TimeRFC1123(nextTime)
+ }
+
+ return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
+}
+
+// Delete operation deletes the specified message.
+//
+// See https://msdn.microsoft.com/en-us/library/azure/dd179347.aspx
+func (m *Message) Delete(options *QueueServiceOptions) error {
+ params := url.Values{"popreceipt": {m.PopReceipt}}
+ headers := m.Queue.qsc.client.getStandardHeaders()
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := m.Queue.qsc.client.getEndpoint(queueServiceName, m.buildPath(), params)
+
+ resp, err := m.Queue.qsc.client.exec(http.MethodDelete, uri, headers, nil, m.Queue.qsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
+}
+
+type putMessageRequest struct {
+ XMLName xml.Name `xml:"QueueMessage"`
+ MessageText string `xml:"MessageText"`
+}
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/odata.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/odata.go
new file mode 100644
index 000000000..41d832e2b
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/odata.go
@@ -0,0 +1,33 @@
+package storage
+
+// MetadataLevel determines if operations should return a paylod,
+// and it level of detail.
+type MetadataLevel string
+
+// This consts are meant to help with Odata supported operations
+const (
+ OdataTypeSuffix = "@odata.type"
+
+ // Types
+
+ OdataBinary = "Edm.Binary"
+ OdataDateTime = "Edm.DateTime"
+ OdataGUID = "Edm.Guid"
+ OdataInt64 = "Edm.Int64"
+
+ // Query options
+
+ OdataFilter = "$filter"
+ OdataOrderBy = "$orderby"
+ OdataTop = "$top"
+ OdataSkip = "$skip"
+ OdataCount = "$count"
+ OdataExpand = "$expand"
+ OdataSelect = "$select"
+ OdataSearch = "$search"
+
+ EmptyPayload MetadataLevel = ""
+ NoMetadata MetadataLevel = "application/json;odata=nometadata"
+ MinimalMetadata MetadataLevel = "application/json;odata=minimalmetadata"
+ FullMetadata MetadataLevel = "application/json;odata=fullmetadata"
+)
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/pageblob.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/pageblob.go
new file mode 100644
index 000000000..bc5b398d3
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/pageblob.go
@@ -0,0 +1,189 @@
+package storage
+
+import (
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "time"
+)
+
+// GetPageRangesResponse contains the response fields from
+// Get Page Ranges call.
+//
+// See https://msdn.microsoft.com/en-us/library/azure/ee691973.aspx
+type GetPageRangesResponse struct {
+ XMLName xml.Name `xml:"PageList"`
+ PageList []PageRange `xml:"PageRange"`
+}
+
+// PageRange contains information about a page of a page blob from
+// Get Pages Range call.
+//
+// See https://msdn.microsoft.com/en-us/library/azure/ee691973.aspx
+type PageRange struct {
+ Start int64 `xml:"Start"`
+ End int64 `xml:"End"`
+}
+
+var (
+ errBlobCopyAborted = errors.New("storage: blob copy is aborted")
+ errBlobCopyIDMismatch = errors.New("storage: blob copy id is a mismatch")
+)
+
+// PutPageOptions includes the options for a put page operation
+type PutPageOptions struct {
+ Timeout uint
+ LeaseID string `header:"x-ms-lease-id"`
+ IfSequenceNumberLessThanOrEqualTo *int `header:"x-ms-if-sequence-number-le"`
+ IfSequenceNumberLessThan *int `header:"x-ms-if-sequence-number-lt"`
+ IfSequenceNumberEqualTo *int `header:"x-ms-if-sequence-number-eq"`
+ IfModifiedSince *time.Time `header:"If-Modified-Since"`
+ IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
+ IfMatch string `header:"If-Match"`
+ IfNoneMatch string `header:"If-None-Match"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// WriteRange writes a range of pages to a page blob.
+// Ranges must be aligned with 512-byte boundaries and chunk must be of size
+// multiplies by 512.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Page
+func (b *Blob) WriteRange(blobRange BlobRange, bytes io.Reader, options *PutPageOptions) error {
+ if bytes == nil {
+ return errors.New("bytes cannot be nil")
+ }
+ return b.modifyRange(blobRange, bytes, options)
+}
+
+// ClearRange clears the given range in a page blob.
+// Ranges must be aligned with 512-byte boundaries and chunk must be of size
+// multiplies by 512.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Page
+func (b *Blob) ClearRange(blobRange BlobRange, options *PutPageOptions) error {
+ return b.modifyRange(blobRange, nil, options)
+}
+
+func (b *Blob) modifyRange(blobRange BlobRange, bytes io.Reader, options *PutPageOptions) error {
+ if blobRange.End < blobRange.Start {
+ return errors.New("the value for rangeEnd must be greater than or equal to rangeStart")
+ }
+ if blobRange.Start%512 != 0 {
+ return errors.New("the value for rangeStart must be a modulus of 512")
+ }
+ if blobRange.End%512 != 511 {
+ return errors.New("the value for rangeEnd must be a modulus of 511")
+ }
+
+ params := url.Values{"comp": {"page"}}
+
+ // default to clear
+ write := "clear"
+ var cl uint64
+
+ // if bytes is not nil then this is an update operation
+ if bytes != nil {
+ write = "update"
+ cl = (blobRange.End - blobRange.Start) + 1
+ }
+
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers["x-ms-blob-type"] = string(BlobTypePage)
+ headers["x-ms-page-write"] = write
+ headers["x-ms-range"] = blobRange.String()
+ headers["Content-Length"] = fmt.Sprintf("%v", cl)
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, bytes, b.Container.bsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+
+ return checkRespCode(resp.statusCode, []int{http.StatusCreated})
+}
+
+// GetPageRangesOptions includes the options for a get page ranges operation
+type GetPageRangesOptions struct {
+ Timeout uint
+ Snapshot *time.Time
+ PreviousSnapshot *time.Time
+ Range *BlobRange
+ LeaseID string `header:"x-ms-lease-id"`
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// GetPageRanges returns the list of valid page ranges for a page blob.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Get-Page-Ranges
+func (b *Blob) GetPageRanges(options *GetPageRangesOptions) (GetPageRangesResponse, error) {
+ params := url.Values{"comp": {"pagelist"}}
+ headers := b.Container.bsc.client.getStandardHeaders()
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ params = addSnapshot(params, options.Snapshot)
+ if options.PreviousSnapshot != nil {
+ params.Add("prevsnapshot", timeRfc1123Formatted(*options.PreviousSnapshot))
+ }
+ if options.Range != nil {
+ headers["Range"] = options.Range.String()
+ }
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ var out GetPageRangesResponse
+ resp, err := b.Container.bsc.client.exec(http.MethodGet, uri, headers, nil, b.Container.bsc.auth)
+ if err != nil {
+ return out, err
+ }
+ defer resp.body.Close()
+
+ if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
+ return out, err
+ }
+ err = xmlUnmarshal(resp.body, &out)
+ return out, err
+}
+
+// PutPageBlob initializes an empty page blob with specified name and maximum
+// size in bytes (size must be aligned to a 512-byte boundary). A page blob must
+// be created using this method before writing pages.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Blob
+func (b *Blob) PutPageBlob(options *PutBlobOptions) error {
+ if b.Properties.ContentLength%512 != 0 {
+ return errors.New("Content length must be aligned to a 512-byte boundary")
+ }
+
+ params := url.Values{}
+ headers := b.Container.bsc.client.getStandardHeaders()
+ headers["x-ms-blob-type"] = string(BlobTypePage)
+ headers["x-ms-blob-content-length"] = fmt.Sprintf("%v", b.Properties.ContentLength)
+ headers["x-ms-blob-sequence-number"] = fmt.Sprintf("%v", b.Properties.SequenceNumber)
+ headers = mergeHeaders(headers, headersFromStruct(b.Properties))
+ headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
+
+ resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusCreated})
+}
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/queue.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/queue.go
new file mode 100644
index 000000000..c2c7f742c
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/queue.go
@@ -0,0 +1,427 @@
+package storage
+
+import (
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strconv"
+ "time"
+)
+
+const (
+ // casing is per Golang's http.Header canonicalizing the header names.
+ approximateMessagesCountHeader = "X-Ms-Approximate-Messages-Count"
+)
+
+// QueueAccessPolicy represents each access policy in the queue ACL.
+type QueueAccessPolicy struct {
+ ID string
+ StartTime time.Time
+ ExpiryTime time.Time
+ CanRead bool
+ CanAdd bool
+ CanUpdate bool
+ CanProcess bool
+}
+
+// QueuePermissions represents the queue ACLs.
+type QueuePermissions struct {
+ AccessPolicies []QueueAccessPolicy
+}
+
+// SetQueuePermissionOptions includes options for a set queue permissions operation
+type SetQueuePermissionOptions struct {
+ Timeout uint
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// Queue represents an Azure queue.
+type Queue struct {
+ qsc *QueueServiceClient
+ Name string
+ Metadata map[string]string
+ AproxMessageCount uint64
+}
+
+func (q *Queue) buildPath() string {
+ return fmt.Sprintf("/%s", q.Name)
+}
+
+func (q *Queue) buildPathMessages() string {
+ return fmt.Sprintf("%s/messages", q.buildPath())
+}
+
+// QueueServiceOptions includes options for some queue service operations
+type QueueServiceOptions struct {
+ Timeout uint
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// Create operation creates a queue under the given account.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Create-Queue4
+func (q *Queue) Create(options *QueueServiceOptions) error {
+ params := url.Values{}
+ headers := q.qsc.client.getStandardHeaders()
+ headers = q.qsc.client.addMetadataToHeaders(headers, q.Metadata)
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := q.qsc.client.getEndpoint(queueServiceName, q.buildPath(), params)
+
+ resp, err := q.qsc.client.exec(http.MethodPut, uri, headers, nil, q.qsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusCreated})
+}
+
+// Delete operation permanently deletes the specified queue.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Delete-Queue3
+func (q *Queue) Delete(options *QueueServiceOptions) error {
+ params := url.Values{}
+ headers := q.qsc.client.getStandardHeaders()
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := q.qsc.client.getEndpoint(queueServiceName, q.buildPath(), params)
+ resp, err := q.qsc.client.exec(http.MethodDelete, uri, headers, nil, q.qsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
+}
+
+// Exists returns true if a queue with given name exists.
+func (q *Queue) Exists() (bool, error) {
+ uri := q.qsc.client.getEndpoint(queueServiceName, q.buildPath(), url.Values{"comp": {"metadata"}})
+ resp, err := q.qsc.client.exec(http.MethodGet, uri, q.qsc.client.getStandardHeaders(), nil, q.qsc.auth)
+ if resp != nil {
+ defer readAndCloseBody(resp.body)
+ if resp.statusCode == http.StatusOK || resp.statusCode == http.StatusNotFound {
+ return resp.statusCode == http.StatusOK, nil
+ }
+ }
+ return false, err
+}
+
+// SetMetadata operation sets user-defined metadata on the specified queue.
+// Metadata is associated with the queue as name-value pairs.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Set-Queue-Metadata
+func (q *Queue) SetMetadata(options *QueueServiceOptions) error {
+ params := url.Values{"comp": {"metadata"}}
+ headers := q.qsc.client.getStandardHeaders()
+ headers = q.qsc.client.addMetadataToHeaders(headers, q.Metadata)
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := q.qsc.client.getEndpoint(queueServiceName, q.buildPath(), params)
+
+ resp, err := q.qsc.client.exec(http.MethodPut, uri, headers, nil, q.qsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
+}
+
+// GetMetadata operation retrieves user-defined metadata and queue
+// properties on the specified queue. Metadata is associated with
+// the queue as name-values pairs.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Set-Queue-Metadata
+//
+// Because the way Golang's http client (and http.Header in particular)
+// canonicalize header names, the returned metadata names would always
+// be all lower case.
+func (q *Queue) GetMetadata(options *QueueServiceOptions) error {
+ params := url.Values{"comp": {"metadata"}}
+ headers := q.qsc.client.getStandardHeaders()
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := q.qsc.client.getEndpoint(queueServiceName, q.buildPath(), url.Values{"comp": {"metadata"}})
+
+ resp, err := q.qsc.client.exec(http.MethodGet, uri, headers, nil, q.qsc.auth)
+ if err != nil {
+ return err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
+ return err
+ }
+
+ aproxMessagesStr := resp.headers.Get(http.CanonicalHeaderKey(approximateMessagesCountHeader))
+ if aproxMessagesStr != "" {
+ aproxMessages, err := strconv.ParseUint(aproxMessagesStr, 10, 64)
+ if err != nil {
+ return err
+ }
+ q.AproxMessageCount = aproxMessages
+ }
+
+ q.Metadata = getMetadataFromHeaders(resp.headers)
+ return nil
+}
+
+// GetMessageReference returns a message object with the specified text.
+func (q *Queue) GetMessageReference(text string) *Message {
+ return &Message{
+ Queue: q,
+ Text: text,
+ }
+}
+
+// GetMessagesOptions is the set of options can be specified for Get
+// Messsages operation. A zero struct does not use any preferences for the
+// request.
+type GetMessagesOptions struct {
+ Timeout uint
+ NumOfMessages int
+ VisibilityTimeout int
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+type messages struct {
+ XMLName xml.Name `xml:"QueueMessagesList"`
+ Messages []Message `xml:"QueueMessage"`
+}
+
+// GetMessages operation retrieves one or more messages from the front of the
+// queue.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Get-Messages
+func (q *Queue) GetMessages(options *GetMessagesOptions) ([]Message, error) {
+ query := url.Values{}
+ headers := q.qsc.client.getStandardHeaders()
+
+ if options != nil {
+ if options.NumOfMessages != 0 {
+ query.Set("numofmessages", strconv.Itoa(options.NumOfMessages))
+ }
+ if options.VisibilityTimeout != 0 {
+ query.Set("visibilitytimeout", strconv.Itoa(options.VisibilityTimeout))
+ }
+ query = addTimeout(query, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := q.qsc.client.getEndpoint(queueServiceName, q.buildPathMessages(), query)
+
+ resp, err := q.qsc.client.exec(http.MethodGet, uri, headers, nil, q.qsc.auth)
+ if err != nil {
+ return []Message{}, err
+ }
+ defer readAndCloseBody(resp.body)
+
+ var out messages
+ err = xmlUnmarshal(resp.body, &out)
+ if err != nil {
+ return []Message{}, err
+ }
+ for i := range out.Messages {
+ out.Messages[i].Queue = q
+ }
+ return out.Messages, err
+}
+
+// PeekMessagesOptions is the set of options can be specified for Peek
+// Messsage operation. A zero struct does not use any preferences for the
+// request.
+type PeekMessagesOptions struct {
+ Timeout uint
+ NumOfMessages int
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// PeekMessages retrieves one or more messages from the front of the queue, but
+// does not alter the visibility of the message.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Peek-Messages
+func (q *Queue) PeekMessages(options *PeekMessagesOptions) ([]Message, error) {
+ query := url.Values{"peekonly": {"true"}} // Required for peek operation
+ headers := q.qsc.client.getStandardHeaders()
+
+ if options != nil {
+ if options.NumOfMessages != 0 {
+ query.Set("numofmessages", strconv.Itoa(options.NumOfMessages))
+ }
+ query = addTimeout(query, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := q.qsc.client.getEndpoint(queueServiceName, q.buildPathMessages(), query)
+
+ resp, err := q.qsc.client.exec(http.MethodGet, uri, headers, nil, q.qsc.auth)
+ if err != nil {
+ return []Message{}, err
+ }
+ defer readAndCloseBody(resp.body)
+
+ var out messages
+ err = xmlUnmarshal(resp.body, &out)
+ if err != nil {
+ return []Message{}, err
+ }
+ for i := range out.Messages {
+ out.Messages[i].Queue = q
+ }
+ return out.Messages, err
+}
+
+// ClearMessages operation deletes all messages from the specified queue.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Clear-Messages
+func (q *Queue) ClearMessages(options *QueueServiceOptions) error {
+ params := url.Values{}
+ headers := q.qsc.client.getStandardHeaders()
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := q.qsc.client.getEndpoint(queueServiceName, q.buildPathMessages(), params)
+
+ resp, err := q.qsc.client.exec(http.MethodDelete, uri, headers, nil, q.qsc.auth)
+ if err != nil {
+ return err
+ }
+ readAndCloseBody(resp.body)
+ return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
+}
+
+// SetPermissions sets up queue permissions
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-queue-acl
+func (q *Queue) SetPermissions(permissions QueuePermissions, options *SetQueuePermissionOptions) error {
+ body, length, err := generateQueueACLpayload(permissions.AccessPolicies)
+ if err != nil {
+ return err
+ }
+
+ params := url.Values{
+ "comp": {"acl"},
+ }
+ headers := q.qsc.client.getStandardHeaders()
+ headers["Content-Length"] = strconv.Itoa(length)
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := q.qsc.client.getEndpoint(queueServiceName, q.buildPath(), params)
+ resp, err := q.qsc.client.exec(http.MethodPut, uri, headers, body, q.qsc.auth)
+ if err != nil {
+ return err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil {
+ return errors.New("Unable to set permissions")
+ }
+
+ return nil
+}
+
+func generateQueueACLpayload(policies []QueueAccessPolicy) (io.Reader, int, error) {
+ sil := SignedIdentifiers{
+ SignedIdentifiers: []SignedIdentifier{},
+ }
+ for _, qapd := range policies {
+ permission := qapd.generateQueuePermissions()
+ signedIdentifier := convertAccessPolicyToXMLStructs(qapd.ID, qapd.StartTime, qapd.ExpiryTime, permission)
+ sil.SignedIdentifiers = append(sil.SignedIdentifiers, signedIdentifier)
+ }
+ return xmlMarshal(sil)
+}
+
+func (qapd *QueueAccessPolicy) generateQueuePermissions() (permissions string) {
+ // generate the permissions string (raup).
+ // still want the end user API to have bool flags.
+ permissions = ""
+
+ if qapd.CanRead {
+ permissions += "r"
+ }
+
+ if qapd.CanAdd {
+ permissions += "a"
+ }
+
+ if qapd.CanUpdate {
+ permissions += "u"
+ }
+
+ if qapd.CanProcess {
+ permissions += "p"
+ }
+
+ return permissions
+}
+
+// GetQueuePermissionOptions includes options for a get queue permissions operation
+type GetQueuePermissionOptions struct {
+ Timeout uint
+ RequestID string `header:"x-ms-client-request-id"`
+}
+
+// GetPermissions gets the queue permissions as per https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-queue-acl
+// If timeout is 0 then it will not be passed to Azure
+func (q *Queue) GetPermissions(options *GetQueuePermissionOptions) (*QueuePermissions, error) {
+ params := url.Values{
+ "comp": {"acl"},
+ }
+ headers := q.qsc.client.getStandardHeaders()
+
+ if options != nil {
+ params = addTimeout(params, options.Timeout)
+ headers = mergeHeaders(headers, headersFromStruct(*options))
+ }
+ uri := q.qsc.client.getEndpoint(queueServiceName, q.buildPath(), params)
+ resp, err := q.qsc.client.exec(http.MethodGet, uri, headers, nil, q.qsc.auth)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.body.Close()
+
+ var ap AccessPolicy
+ err = xmlUnmarshal(resp.body, &ap.SignedIdentifiersList)
+ if err != nil {
+ return nil, err
+ }
+ return buildQueueAccessPolicy(ap, &resp.headers), nil
+}
+
+func buildQueueAccessPolicy(ap AccessPolicy, headers *http.Header) *QueuePermissions {
+ permissions := QueuePermissions{
+ AccessPolicies: []QueueAccessPolicy{},
+ }
+
+ for _, policy := range ap.SignedIdentifiersList.SignedIdentifiers {
+ qapd := QueueAccessPolicy{
+ ID: policy.ID,
+ StartTime: policy.AccessPolicy.StartTime,
+ ExpiryTime: policy.AccessPolicy.ExpiryTime,
+ }
+ qapd.CanRead = updatePermissions(policy.AccessPolicy.Permission, "r")
+ qapd.CanAdd = updatePermissions(policy.AccessPolicy.Permission, "a")
+ qapd.CanUpdate = updatePermissions(policy.AccessPolicy.Permission, "u")
+ qapd.CanProcess = updatePermissions(policy.AccessPolicy.Permission, "p")
+
+ permissions.AccessPolicies = append(permissions.AccessPolicies, qapd)
+ }
+ return &permissions
+}
diff --git a/vendor/github.com/Azure/azure-storage-go/queueserviceclient.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/queueserviceclient.go
similarity index 57%
rename from vendor/github.com/Azure/azure-storage-go/queueserviceclient.go
rename to vendor/github.com/Azure/azure-sdk-for-go/storage/queueserviceclient.go
index c26141339..19b44941c 100644
--- a/vendor/github.com/Azure/azure-storage-go/queueserviceclient.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/queueserviceclient.go
@@ -9,12 +9,20 @@ type QueueServiceClient struct {
// GetServiceProperties gets the properties of your storage account's queue service.
// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-queue-service-properties
-func (c *QueueServiceClient) GetServiceProperties() (*ServiceProperties, error) {
- return c.client.getServiceProperties(queueServiceName, c.auth)
+func (q *QueueServiceClient) GetServiceProperties() (*ServiceProperties, error) {
+ return q.client.getServiceProperties(queueServiceName, q.auth)
}
// SetServiceProperties sets the properties of your storage account's queue service.
// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-queue-service-properties
-func (c *QueueServiceClient) SetServiceProperties(props ServiceProperties) error {
- return c.client.setServiceProperties(props, queueServiceName, c.auth)
+func (q *QueueServiceClient) SetServiceProperties(props ServiceProperties) error {
+ return q.client.setServiceProperties(props, queueServiceName, q.auth)
+}
+
+// GetQueueReference returns a Container object for the specified queue name.
+func (q *QueueServiceClient) GetQueueReference(name string) *Queue {
+ return &Queue{
+ qsc: q,
+ Name: name,
+ }
}
diff --git a/vendor/github.com/Azure/azure-storage-go/share.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/share.go
similarity index 67%
rename from vendor/github.com/Azure/azure-storage-go/share.go
rename to vendor/github.com/Azure/azure-sdk-for-go/storage/share.go
index e190097ea..e6a868081 100644
--- a/vendor/github.com/Azure/azure-storage-go/share.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/share.go
@@ -30,9 +30,15 @@ func (s *Share) buildPath() string {
// Create this share under the associated account.
// If a share with the same name already exists, the operation fails.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn167008.aspx
-func (s *Share) Create() error {
- headers, err := s.fsc.createResource(s.buildPath(), resourceShare, nil, mergeMDIntoExtraHeaders(s.Metadata, nil), []int{http.StatusCreated})
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Create-Share
+func (s *Share) Create(options *FileRequestOptions) error {
+ extraheaders := map[string]string{}
+ if s.Properties.Quota > 0 {
+ extraheaders["x-ms-share-quota"] = strconv.Itoa(s.Properties.Quota)
+ }
+
+ params := prepareOptions(options)
+ headers, err := s.fsc.createResource(s.buildPath(), resourceShare, params, mergeMDIntoExtraHeaders(s.Metadata, extraheaders), []int{http.StatusCreated})
if err != nil {
return err
}
@@ -45,9 +51,15 @@ func (s *Share) Create() error {
// it does not exist. Returns true if the share is newly created or false if
// the share already exists.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn167008.aspx
-func (s *Share) CreateIfNotExists() (bool, error) {
- resp, err := s.fsc.createResourceNoClose(s.buildPath(), resourceShare, nil, nil)
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Create-Share
+func (s *Share) CreateIfNotExists(options *FileRequestOptions) (bool, error) {
+ extraheaders := map[string]string{}
+ if s.Properties.Quota > 0 {
+ extraheaders["x-ms-share-quota"] = strconv.Itoa(s.Properties.Quota)
+ }
+
+ params := prepareOptions(options)
+ resp, err := s.fsc.createResourceNoClose(s.buildPath(), resourceShare, params, extraheaders)
if resp != nil {
defer readAndCloseBody(resp.body)
if resp.statusCode == http.StatusCreated || resp.statusCode == http.StatusConflict {
@@ -55,7 +67,7 @@ func (s *Share) CreateIfNotExists() (bool, error) {
s.updateEtagAndLastModified(resp.headers)
return true, nil
}
- return false, s.FetchAttributes()
+ return false, s.FetchAttributes(nil)
}
}
@@ -66,16 +78,16 @@ func (s *Share) CreateIfNotExists() (bool, error) {
// and directories contained within it are later deleted during garbage
// collection. If the share does not exist the operation fails
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn689090.aspx
-func (s *Share) Delete() error {
- return s.fsc.deleteResource(s.buildPath(), resourceShare)
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Delete-Share
+func (s *Share) Delete(options *FileRequestOptions) error {
+ return s.fsc.deleteResource(s.buildPath(), resourceShare, options)
}
// DeleteIfExists operation marks this share for deletion if it exists.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dn689090.aspx
-func (s *Share) DeleteIfExists() (bool, error) {
- resp, err := s.fsc.deleteResourceNoClose(s.buildPath(), resourceShare)
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Delete-Share
+func (s *Share) DeleteIfExists(options *FileRequestOptions) (bool, error) {
+ resp, err := s.fsc.deleteResourceNoClose(s.buildPath(), resourceShare, options)
if resp != nil {
defer readAndCloseBody(resp.body)
if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound {
@@ -97,8 +109,10 @@ func (s *Share) Exists() (bool, error) {
}
// FetchAttributes retrieves metadata and properties for this share.
-func (s *Share) FetchAttributes() error {
- headers, err := s.fsc.getResourceHeaders(s.buildPath(), compNone, resourceShare, http.MethodHead)
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-share-properties
+func (s *Share) FetchAttributes(options *FileRequestOptions) error {
+ params := prepareOptions(options)
+ headers, err := s.fsc.getResourceHeaders(s.buildPath(), compNone, resourceShare, params, http.MethodHead)
if err != nil {
return err
}
@@ -130,9 +144,9 @@ func (s *Share) ServiceClient() *FileServiceClient {
// are case-insensitive so case munging should not matter to other
// applications either.
//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx
-func (s *Share) SetMetadata() error {
- headers, err := s.fsc.setResourceHeaders(s.buildPath(), compMetadata, resourceShare, mergeMDIntoExtraHeaders(s.Metadata, nil))
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-share-metadata
+func (s *Share) SetMetadata(options *FileRequestOptions) error {
+ headers, err := s.fsc.setResourceHeaders(s.buildPath(), compMetadata, resourceShare, mergeMDIntoExtraHeaders(s.Metadata, nil), options)
if err != nil {
return err
}
@@ -148,15 +162,17 @@ func (s *Share) SetMetadata() error {
// are case-insensitive so case munging should not matter to other
// applications either.
//
-// See https://msdn.microsoft.com/en-us/library/azure/mt427368.aspx
-func (s *Share) SetProperties() error {
- if s.Properties.Quota < 1 || s.Properties.Quota > 5120 {
- return fmt.Errorf("invalid value %v for quota, valid values are [1, 5120]", s.Properties.Quota)
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Set-Share-Properties
+func (s *Share) SetProperties(options *FileRequestOptions) error {
+ extraheaders := map[string]string{}
+ if s.Properties.Quota > 0 {
+ if s.Properties.Quota > 5120 {
+ return fmt.Errorf("invalid value %v for quota, valid values are [1, 5120]", s.Properties.Quota)
+ }
+ extraheaders["x-ms-share-quota"] = strconv.Itoa(s.Properties.Quota)
}
- headers, err := s.fsc.setResourceHeaders(s.buildPath(), compProperties, resourceShare, map[string]string{
- "x-ms-share-quota": strconv.Itoa(s.Properties.Quota),
- })
+ headers, err := s.fsc.setResourceHeaders(s.buildPath(), compProperties, resourceShare, extraheaders, options)
if err != nil {
return err
}
diff --git a/vendor/github.com/Azure/azure-storage-go/storagepolicy.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/storagepolicy.go
similarity index 100%
rename from vendor/github.com/Azure/azure-storage-go/storagepolicy.go
rename to vendor/github.com/Azure/azure-sdk-for-go/storage/storagepolicy.go
diff --git a/vendor/github.com/Azure/azure-storage-go/storageservice.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/storageservice.go
similarity index 96%
rename from vendor/github.com/Azure/azure-storage-go/storageservice.go
rename to vendor/github.com/Azure/azure-sdk-for-go/storage/storageservice.go
index 817560b78..88700fbc9 100644
--- a/vendor/github.com/Azure/azure-storage-go/storageservice.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/storageservice.go
@@ -1,9 +1,9 @@
package storage
import (
- "fmt"
"net/http"
"net/url"
+ "strconv"
)
// ServiceProperties represents the storage account service properties
@@ -106,13 +106,12 @@ func (c Client) setServiceProperties(props ServiceProperties, service string, au
}
headers := c.getStandardHeaders()
- headers["Content-Length"] = fmt.Sprintf("%v", length)
+ headers["Content-Length"] = strconv.Itoa(length)
resp, err := c.exec(http.MethodPut, uri, headers, body, auth)
if err != nil {
return err
}
- defer readAndCloseBody(resp.body)
-
+ readAndCloseBody(resp.body)
return checkRespCode(resp.statusCode, []int{http.StatusAccepted})
}
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/table.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/table.go
new file mode 100644
index 000000000..4eae3af9d
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/table.go
@@ -0,0 +1,412 @@
+package storage
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+)
+
+const (
+ tablesURIPath = "/Tables"
+ nextTableQueryParameter = "NextTableName"
+ headerNextPartitionKey = "x-ms-continuation-NextPartitionKey"
+ headerNextRowKey = "x-ms-continuation-NextRowKey"
+ nextPartitionKeyQueryParameter = "NextPartitionKey"
+ nextRowKeyQueryParameter = "NextRowKey"
+)
+
+// TableAccessPolicy are used for SETTING table policies
+type TableAccessPolicy struct {
+ ID string
+ StartTime time.Time
+ ExpiryTime time.Time
+ CanRead bool
+ CanAppend bool
+ CanUpdate bool
+ CanDelete bool
+}
+
+// Table represents an Azure table.
+type Table struct {
+ tsc *TableServiceClient
+ Name string `json:"TableName"`
+ OdataEditLink string `json:"odata.editLink"`
+ OdataID string `json:"odata.id"`
+ OdataMetadata string `json:"odata.metadata"`
+ OdataType string `json:"odata.type"`
+}
+
+// EntityQueryResult contains the response from
+// ExecuteQuery and ExecuteQueryNextResults functions.
+type EntityQueryResult struct {
+ OdataMetadata string `json:"odata.metadata"`
+ Entities []*Entity `json:"value"`
+ QueryNextLink
+ table *Table
+}
+
+type continuationToken struct {
+ NextPartitionKey string
+ NextRowKey string
+}
+
+func (t *Table) buildPath() string {
+ return fmt.Sprintf("/%s", t.Name)
+}
+
+func (t *Table) buildSpecificPath() string {
+ return fmt.Sprintf("%s('%s')", tablesURIPath, t.Name)
+}
+
+// Get gets the referenced table.
+// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/querying-tables-and-entities
+func (t *Table) Get(timeout uint, ml MetadataLevel) error {
+ if ml == EmptyPayload {
+ return errEmptyPayload
+ }
+
+ query := url.Values{
+ "timeout": {strconv.FormatUint(uint64(timeout), 10)},
+ }
+ headers := t.tsc.client.getStandardHeaders()
+ headers[headerAccept] = string(ml)
+
+ uri := t.tsc.client.getEndpoint(tableServiceName, t.buildSpecificPath(), query)
+ resp, err := t.tsc.client.exec(http.MethodGet, uri, headers, nil, t.tsc.auth)
+ if err != nil {
+ return err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
+ return err
+ }
+
+ respBody, err := ioutil.ReadAll(resp.body)
+ if err != nil {
+ return err
+ }
+ err = json.Unmarshal(respBody, t)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// Create creates the referenced table.
+// This function fails if the name is not compliant
+// with the specification or the tables already exists.
+// ml determines the level of detail of metadata in the operation response,
+// or no data at all.
+// See https://docs.microsoft.com/rest/api/storageservices/fileservices/create-table
+func (t *Table) Create(timeout uint, ml MetadataLevel, options *TableOptions) error {
+ uri := t.tsc.client.getEndpoint(tableServiceName, tablesURIPath, url.Values{
+ "timeout": {strconv.FormatUint(uint64(timeout), 10)},
+ })
+
+ type createTableRequest struct {
+ TableName string `json:"TableName"`
+ }
+ req := createTableRequest{TableName: t.Name}
+ buf := new(bytes.Buffer)
+ if err := json.NewEncoder(buf).Encode(req); err != nil {
+ return err
+ }
+
+ headers := t.tsc.client.getStandardHeaders()
+ headers = addReturnContentHeaders(headers, ml)
+ headers = addBodyRelatedHeaders(headers, buf.Len())
+ headers = options.addToHeaders(headers)
+
+ resp, err := t.tsc.client.exec(http.MethodPost, uri, headers, buf, t.tsc.auth)
+ if err != nil {
+ return err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if ml == EmptyPayload {
+ if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil {
+ return err
+ }
+ } else {
+ if err := checkRespCode(resp.statusCode, []int{http.StatusCreated}); err != nil {
+ return err
+ }
+ }
+
+ if ml != EmptyPayload {
+ data, err := ioutil.ReadAll(resp.body)
+ if err != nil {
+ return err
+ }
+ err = json.Unmarshal(data, t)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// Delete deletes the referenced table.
+// This function fails if the table is not present.
+// Be advised: Delete deletes all the entries that may be present.
+// See https://docs.microsoft.com/rest/api/storageservices/fileservices/delete-table
+func (t *Table) Delete(timeout uint, options *TableOptions) error {
+ uri := t.tsc.client.getEndpoint(tableServiceName, t.buildSpecificPath(), url.Values{
+ "timeout": {strconv.Itoa(int(timeout))},
+ })
+
+ headers := t.tsc.client.getStandardHeaders()
+ headers = addReturnContentHeaders(headers, EmptyPayload)
+ headers = options.addToHeaders(headers)
+
+ resp, err := t.tsc.client.exec(http.MethodDelete, uri, headers, nil, t.tsc.auth)
+ if err != nil {
+ return err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil {
+ return err
+
+ }
+ return nil
+}
+
+// QueryOptions includes options for a query entities operation.
+// Top, filter and select are OData query options.
+type QueryOptions struct {
+ Top uint
+ Filter string
+ Select []string
+ RequestID string
+}
+
+func (options *QueryOptions) getParameters() (url.Values, map[string]string) {
+ query := url.Values{}
+ headers := map[string]string{}
+ if options != nil {
+ if options.Top > 0 {
+ query.Add(OdataTop, strconv.FormatUint(uint64(options.Top), 10))
+ }
+ if options.Filter != "" {
+ query.Add(OdataFilter, options.Filter)
+ }
+ if len(options.Select) > 0 {
+ query.Add(OdataSelect, strings.Join(options.Select, ","))
+ }
+ headers = addToHeaders(headers, "x-ms-client-request-id", options.RequestID)
+ }
+ return query, headers
+}
+
+// QueryEntities returns the entities in the table.
+// You can use query options defined by the OData Protocol specification.
+//
+// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/query-entities
+func (t *Table) QueryEntities(timeout uint, ml MetadataLevel, options *QueryOptions) (*EntityQueryResult, error) {
+ if ml == EmptyPayload {
+ return nil, errEmptyPayload
+ }
+ query, headers := options.getParameters()
+ query = addTimeout(query, timeout)
+ uri := t.tsc.client.getEndpoint(tableServiceName, t.buildPath(), query)
+ return t.queryEntities(uri, headers, ml)
+}
+
+// NextResults returns the next page of results
+// from a QueryEntities or NextResults operation.
+//
+// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/query-entities
+// See https://docs.microsoft.com/rest/api/storageservices/fileservices/query-timeout-and-pagination
+func (eqr *EntityQueryResult) NextResults(options *TableOptions) (*EntityQueryResult, error) {
+ if eqr == nil {
+ return nil, errNilPreviousResult
+ }
+ if eqr.NextLink == nil {
+ return nil, errNilNextLink
+ }
+ headers := options.addToHeaders(map[string]string{})
+ return eqr.table.queryEntities(*eqr.NextLink, headers, eqr.ml)
+}
+
+// SetPermissions sets up table ACL permissions
+// See https://docs.microsoft.com/rest/api/storageservices/fileservices/Set-Table-ACL
+func (t *Table) SetPermissions(tap []TableAccessPolicy, timeout uint, options *TableOptions) error {
+ params := url.Values{"comp": {"acl"},
+ "timeout": {strconv.Itoa(int(timeout))},
+ }
+
+ uri := t.tsc.client.getEndpoint(tableServiceName, t.Name, params)
+ headers := t.tsc.client.getStandardHeaders()
+ headers = options.addToHeaders(headers)
+
+ body, length, err := generateTableACLPayload(tap)
+ if err != nil {
+ return err
+ }
+ headers["Content-Length"] = strconv.Itoa(length)
+
+ resp, err := t.tsc.client.exec(http.MethodPut, uri, headers, body, t.tsc.auth)
+ if err != nil {
+ return err
+ }
+ defer readAndCloseBody(resp.body)
+
+ if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil {
+ return err
+ }
+ return nil
+}
+
+func generateTableACLPayload(policies []TableAccessPolicy) (io.Reader, int, error) {
+ sil := SignedIdentifiers{
+ SignedIdentifiers: []SignedIdentifier{},
+ }
+ for _, tap := range policies {
+ permission := generateTablePermissions(&tap)
+ signedIdentifier := convertAccessPolicyToXMLStructs(tap.ID, tap.StartTime, tap.ExpiryTime, permission)
+ sil.SignedIdentifiers = append(sil.SignedIdentifiers, signedIdentifier)
+ }
+ return xmlMarshal(sil)
+}
+
+// GetPermissions gets the table ACL permissions
+// See https://docs.microsoft.com/rest/api/storageservices/fileservices/get-table-acl
+func (t *Table) GetPermissions(timeout int, options *TableOptions) ([]TableAccessPolicy, error) {
+ params := url.Values{"comp": {"acl"},
+ "timeout": {strconv.Itoa(int(timeout))},
+ }
+
+ uri := t.tsc.client.getEndpoint(tableServiceName, t.Name, params)
+ headers := t.tsc.client.getStandardHeaders()
+ headers = options.addToHeaders(headers)
+
+ resp, err := t.tsc.client.exec(http.MethodGet, uri, headers, nil, t.tsc.auth)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.body.Close()
+
+ if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
+ return nil, err
+ }
+
+ var ap AccessPolicy
+ err = xmlUnmarshal(resp.body, &ap.SignedIdentifiersList)
+ if err != nil {
+ return nil, err
+ }
+ return updateTableAccessPolicy(ap), nil
+}
+
+func (t *Table) queryEntities(uri string, headers map[string]string, ml MetadataLevel) (*EntityQueryResult, error) {
+ headers = mergeHeaders(headers, t.tsc.client.getStandardHeaders())
+ if ml != EmptyPayload {
+ headers[headerAccept] = string(ml)
+ }
+
+ resp, err := t.tsc.client.exec(http.MethodGet, uri, headers, nil, t.tsc.auth)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.body.Close()
+
+ if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
+ return nil, err
+ }
+
+ data, err := ioutil.ReadAll(resp.body)
+ if err != nil {
+ return nil, err
+ }
+ var entities EntityQueryResult
+ err = json.Unmarshal(data, &entities)
+ if err != nil {
+ return nil, err
+ }
+
+ for i := range entities.Entities {
+ entities.Entities[i].Table = t
+ }
+ entities.table = t
+
+ contToken := extractContinuationTokenFromHeaders(resp.headers)
+ if contToken == nil {
+ entities.NextLink = nil
+ } else {
+ originalURI, err := url.Parse(uri)
+ if err != nil {
+ return nil, err
+ }
+ v := originalURI.Query()
+ v.Set(nextPartitionKeyQueryParameter, contToken.NextPartitionKey)
+ v.Set(nextRowKeyQueryParameter, contToken.NextRowKey)
+ newURI := t.tsc.client.getEndpoint(tableServiceName, t.buildPath(), v)
+ entities.NextLink = &newURI
+ entities.ml = ml
+ }
+
+ return &entities, nil
+}
+
+func extractContinuationTokenFromHeaders(h http.Header) *continuationToken {
+ ct := continuationToken{
+ NextPartitionKey: h.Get(headerNextPartitionKey),
+ NextRowKey: h.Get(headerNextRowKey),
+ }
+
+ if ct.NextPartitionKey != "" && ct.NextRowKey != "" {
+ return &ct
+ }
+ return nil
+}
+
+func updateTableAccessPolicy(ap AccessPolicy) []TableAccessPolicy {
+ taps := []TableAccessPolicy{}
+ for _, policy := range ap.SignedIdentifiersList.SignedIdentifiers {
+ tap := TableAccessPolicy{
+ ID: policy.ID,
+ StartTime: policy.AccessPolicy.StartTime,
+ ExpiryTime: policy.AccessPolicy.ExpiryTime,
+ }
+ tap.CanRead = updatePermissions(policy.AccessPolicy.Permission, "r")
+ tap.CanAppend = updatePermissions(policy.AccessPolicy.Permission, "a")
+ tap.CanUpdate = updatePermissions(policy.AccessPolicy.Permission, "u")
+ tap.CanDelete = updatePermissions(policy.AccessPolicy.Permission, "d")
+
+ taps = append(taps, tap)
+ }
+ return taps
+}
+
+func generateTablePermissions(tap *TableAccessPolicy) (permissions string) {
+ // generate the permissions string (raud).
+ // still want the end user API to have bool flags.
+ permissions = ""
+
+ if tap.CanRead {
+ permissions += "r"
+ }
+
+ if tap.CanAppend {
+ permissions += "a"
+ }
+
+ if tap.CanUpdate {
+ permissions += "u"
+ }
+
+ if tap.CanDelete {
+ permissions += "d"
+ }
+ return permissions
+}
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/table_batch.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/table_batch.go
new file mode 100644
index 000000000..7a0f0915c
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/table_batch.go
@@ -0,0 +1,302 @@
+package storage
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "mime/multipart"
+ "net/http"
+ "net/textproto"
+ "sort"
+ "strings"
+
+ "github.com/satori/uuid"
+)
+
+// Operation type. Insert, Delete, Replace etc.
+type Operation int
+
+// consts for batch operations.
+const (
+ InsertOp = Operation(1)
+ DeleteOp = Operation(2)
+ ReplaceOp = Operation(3)
+ MergeOp = Operation(4)
+ InsertOrReplaceOp = Operation(5)
+ InsertOrMergeOp = Operation(6)
+)
+
+// BatchEntity used for tracking Entities to operate on and
+// whether operations (replace/merge etc) should be forced.
+// Wrapper for regular Entity with additional data specific for the entity.
+type BatchEntity struct {
+ *Entity
+ Force bool
+ Op Operation
+}
+
+// TableBatch stores all the entities that will be operated on during a batch process.
+// Entities can be inserted, replaced or deleted.
+type TableBatch struct {
+ BatchEntitySlice []BatchEntity
+
+ // reference to table we're operating on.
+ Table *Table
+}
+
+// defaultChangesetHeaders for changeSets
+var defaultChangesetHeaders = map[string]string{
+ "Accept": "application/json;odata=minimalmetadata",
+ "Content-Type": "application/json",
+ "Prefer": "return-no-content",
+}
+
+// NewBatch return new TableBatch for populating.
+func (t *Table) NewBatch() *TableBatch {
+ return &TableBatch{
+ Table: t,
+ }
+}
+
+// InsertEntity adds an entity in preparation for a batch insert.
+func (t *TableBatch) InsertEntity(entity *Entity) {
+ be := BatchEntity{Entity: entity, Force: false, Op: InsertOp}
+ t.BatchEntitySlice = append(t.BatchEntitySlice, be)
+}
+
+// InsertOrReplaceEntity adds an entity in preparation for a batch insert or replace.
+func (t *TableBatch) InsertOrReplaceEntity(entity *Entity, force bool) {
+ be := BatchEntity{Entity: entity, Force: false, Op: InsertOrReplaceOp}
+ t.BatchEntitySlice = append(t.BatchEntitySlice, be)
+}
+
+// InsertOrReplaceEntityByForce adds an entity in preparation for a batch insert or replace. Forces regardless of ETag
+func (t *TableBatch) InsertOrReplaceEntityByForce(entity *Entity) {
+ t.InsertOrReplaceEntity(entity, true)
+}
+
+// InsertOrMergeEntity adds an entity in preparation for a batch insert or merge.
+func (t *TableBatch) InsertOrMergeEntity(entity *Entity, force bool) {
+ be := BatchEntity{Entity: entity, Force: false, Op: InsertOrMergeOp}
+ t.BatchEntitySlice = append(t.BatchEntitySlice, be)
+}
+
+// InsertOrMergeEntityByForce adds an entity in preparation for a batch insert or merge. Forces regardless of ETag
+func (t *TableBatch) InsertOrMergeEntityByForce(entity *Entity) {
+ t.InsertOrMergeEntity(entity, true)
+}
+
+// ReplaceEntity adds an entity in preparation for a batch replace.
+func (t *TableBatch) ReplaceEntity(entity *Entity) {
+ be := BatchEntity{Entity: entity, Force: false, Op: ReplaceOp}
+ t.BatchEntitySlice = append(t.BatchEntitySlice, be)
+}
+
+// DeleteEntity adds an entity in preparation for a batch delete
+func (t *TableBatch) DeleteEntity(entity *Entity, force bool) {
+ be := BatchEntity{Entity: entity, Force: false, Op: DeleteOp}
+ t.BatchEntitySlice = append(t.BatchEntitySlice, be)
+}
+
+// DeleteEntityByForce adds an entity in preparation for a batch delete. Forces regardless of ETag
+func (t *TableBatch) DeleteEntityByForce(entity *Entity, force bool) {
+ t.DeleteEntity(entity, true)
+}
+
+// MergeEntity adds an entity in preparation for a batch merge
+func (t *TableBatch) MergeEntity(entity *Entity) {
+ be := BatchEntity{Entity: entity, Force: false, Op: MergeOp}
+ t.BatchEntitySlice = append(t.BatchEntitySlice, be)
+}
+
+// ExecuteBatch executes many table operations in one request to Azure.
+// The operations can be combinations of Insert, Delete, Replace and Merge
+// Creates the inner changeset body (various operations, Insert, Delete etc) then creates the outer request packet that encompasses
+// the changesets.
+// As per document https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/performing-entity-group-transactions
+func (t *TableBatch) ExecuteBatch() error {
+ changesetBoundary := fmt.Sprintf("changeset_%s", uuid.NewV1())
+ uri := t.Table.tsc.client.getEndpoint(tableServiceName, "$batch", nil)
+ changesetBody, err := t.generateChangesetBody(changesetBoundary)
+ if err != nil {
+ return err
+ }
+
+ boundary := fmt.Sprintf("batch_%s", uuid.NewV1())
+ body, err := generateBody(changesetBody, changesetBoundary, boundary)
+ if err != nil {
+ return err
+ }
+
+ headers := t.Table.tsc.client.getStandardHeaders()
+ headers[headerContentType] = fmt.Sprintf("multipart/mixed; boundary=%s", boundary)
+
+ resp, err := t.Table.tsc.client.execBatchOperationJSON(http.MethodPost, uri, headers, bytes.NewReader(body.Bytes()), t.Table.tsc.auth)
+ if err != nil {
+ return err
+ }
+ defer resp.body.Close()
+
+ if err = checkRespCode(resp.statusCode, []int{http.StatusAccepted}); err != nil {
+
+ // check which batch failed.
+ operationFailedMessage := t.getFailedOperation(resp.odata.Err.Message.Value)
+ requestID, date, version := getDebugHeaders(resp.headers)
+ return AzureStorageServiceError{
+ StatusCode: resp.statusCode,
+ Code: resp.odata.Err.Code,
+ RequestID: requestID,
+ Date: date,
+ APIVersion: version,
+ Message: operationFailedMessage,
+ }
+ }
+
+ return nil
+}
+
+// getFailedOperation parses the original Azure error string and determines which operation failed
+// and generates appropriate message.
+func (t *TableBatch) getFailedOperation(errorMessage string) string {
+ // errorMessage consists of "number:string" we just need the number.
+ sp := strings.Split(errorMessage, ":")
+ if len(sp) > 1 {
+ msg := fmt.Sprintf("Element %s in the batch returned an unexpected response code.\n%s", sp[0], errorMessage)
+ return msg
+ }
+
+ // cant parse the message, just return the original message to client
+ return errorMessage
+}
+
+// generateBody generates the complete body for the batch request.
+func generateBody(changeSetBody *bytes.Buffer, changesetBoundary string, boundary string) (*bytes.Buffer, error) {
+
+ body := new(bytes.Buffer)
+ writer := multipart.NewWriter(body)
+ writer.SetBoundary(boundary)
+ h := make(textproto.MIMEHeader)
+ h.Set(headerContentType, fmt.Sprintf("multipart/mixed; boundary=%s\r\n", changesetBoundary))
+ batchWriter, err := writer.CreatePart(h)
+ if err != nil {
+ return nil, err
+ }
+ batchWriter.Write(changeSetBody.Bytes())
+ writer.Close()
+ return body, nil
+}
+
+// generateChangesetBody generates the individual changesets for the various operations within the batch request.
+// There is a changeset for Insert, Delete, Merge etc.
+func (t *TableBatch) generateChangesetBody(changesetBoundary string) (*bytes.Buffer, error) {
+
+ body := new(bytes.Buffer)
+ writer := multipart.NewWriter(body)
+ writer.SetBoundary(changesetBoundary)
+
+ for _, be := range t.BatchEntitySlice {
+ t.generateEntitySubset(&be, writer)
+ }
+
+ writer.Close()
+ return body, nil
+}
+
+// generateVerb generates the HTTP request VERB required for each changeset.
+func generateVerb(op Operation) (string, error) {
+ switch op {
+ case InsertOp:
+ return http.MethodPost, nil
+ case DeleteOp:
+ return http.MethodDelete, nil
+ case ReplaceOp, InsertOrReplaceOp:
+ return http.MethodPut, nil
+ case MergeOp, InsertOrMergeOp:
+ return "MERGE", nil
+ default:
+ return "", errors.New("Unable to detect operation")
+ }
+}
+
+// generateQueryPath generates the query path for within the changesets
+// For inserts it will just be a table query path (table name)
+// but for other operations (modifying an existing entity) then
+// the partition/row keys need to be generated.
+func (t *TableBatch) generateQueryPath(op Operation, entity *Entity) string {
+ if op == InsertOp {
+ return entity.Table.buildPath()
+ }
+ return entity.buildPath()
+}
+
+// generateGenericOperationHeaders generates common headers for a given operation.
+func generateGenericOperationHeaders(be *BatchEntity) map[string]string {
+ retval := map[string]string{}
+
+ for k, v := range defaultChangesetHeaders {
+ retval[k] = v
+ }
+
+ if be.Op == DeleteOp || be.Op == ReplaceOp || be.Op == MergeOp {
+ if be.Force || be.Entity.OdataEtag == "" {
+ retval["If-Match"] = "*"
+ } else {
+ retval["If-Match"] = be.Entity.OdataEtag
+ }
+ }
+
+ return retval
+}
+
+// generateEntitySubset generates body payload for particular batch entity
+func (t *TableBatch) generateEntitySubset(batchEntity *BatchEntity, writer *multipart.Writer) error {
+
+ h := make(textproto.MIMEHeader)
+ h.Set(headerContentType, "application/http")
+ h.Set(headerContentTransferEncoding, "binary")
+
+ verb, err := generateVerb(batchEntity.Op)
+ if err != nil {
+ return err
+ }
+
+ genericOpHeadersMap := generateGenericOperationHeaders(batchEntity)
+ queryPath := t.generateQueryPath(batchEntity.Op, batchEntity.Entity)
+ uri := t.Table.tsc.client.getEndpoint(tableServiceName, queryPath, nil)
+
+ operationWriter, err := writer.CreatePart(h)
+ if err != nil {
+ return err
+ }
+
+ urlAndVerb := fmt.Sprintf("%s %s HTTP/1.1\r\n", verb, uri)
+ operationWriter.Write([]byte(urlAndVerb))
+ writeHeaders(genericOpHeadersMap, &operationWriter)
+ operationWriter.Write([]byte("\r\n")) // additional \r\n is needed per changeset separating the "headers" and the body.
+
+ // delete operation doesn't need a body.
+ if batchEntity.Op != DeleteOp {
+ //var e Entity = batchEntity.Entity
+ body, err := json.Marshal(batchEntity.Entity)
+ if err != nil {
+ return err
+ }
+ operationWriter.Write(body)
+ }
+
+ return nil
+}
+
+func writeHeaders(h map[string]string, writer *io.Writer) {
+ // This way it is guaranteed the headers will be written in a sorted order
+ var keys []string
+ for k := range h {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ (*writer).Write([]byte(fmt.Sprintf("%s: %s\r\n", k, h[k])))
+ }
+}
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/tableserviceclient.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/tableserviceclient.go
new file mode 100644
index 000000000..895dcfded
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/tableserviceclient.go
@@ -0,0 +1,190 @@
+package storage
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strconv"
+)
+
+const (
+ headerAccept = "Accept"
+ headerEtag = "Etag"
+ headerPrefer = "Prefer"
+ headerXmsContinuation = "x-ms-Continuation-NextTableName"
+)
+
+// TableServiceClient contains operations for Microsoft Azure Table Storage
+// Service.
+type TableServiceClient struct {
+ client Client
+ auth authentication
+}
+
+// TableOptions includes options for some table operations
+type TableOptions struct {
+ RequestID string
+}
+
+func (options *TableOptions) addToHeaders(h map[string]string) map[string]string {
+ if options != nil {
+ h = addToHeaders(h, "x-ms-client-request-id", options.RequestID)
+ }
+ return h
+}
+
+// QueryNextLink includes information for getting the next page of
+// results in query operations
+type QueryNextLink struct {
+ NextLink *string
+ ml MetadataLevel
+}
+
+// GetServiceProperties gets the properties of your storage account's table service.
+// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-table-service-properties
+func (t *TableServiceClient) GetServiceProperties() (*ServiceProperties, error) {
+ return t.client.getServiceProperties(tableServiceName, t.auth)
+}
+
+// SetServiceProperties sets the properties of your storage account's table service.
+// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-table-service-properties
+func (t *TableServiceClient) SetServiceProperties(props ServiceProperties) error {
+ return t.client.setServiceProperties(props, tableServiceName, t.auth)
+}
+
+// GetTableReference returns a Table object for the specified table name.
+func (t *TableServiceClient) GetTableReference(name string) *Table {
+ return &Table{
+ tsc: t,
+ Name: name,
+ }
+}
+
+// QueryTablesOptions includes options for some table operations
+type QueryTablesOptions struct {
+ Top uint
+ Filter string
+ RequestID string
+}
+
+func (options *QueryTablesOptions) getParameters() (url.Values, map[string]string) {
+ query := url.Values{}
+ headers := map[string]string{}
+ if options != nil {
+ if options.Top > 0 {
+ query.Add(OdataTop, strconv.FormatUint(uint64(options.Top), 10))
+ }
+ if options.Filter != "" {
+ query.Add(OdataFilter, options.Filter)
+ }
+ headers = addToHeaders(headers, "x-ms-client-request-id", options.RequestID)
+ }
+ return query, headers
+}
+
+// QueryTables returns the tables in the storage account.
+// You can use query options defined by the OData Protocol specification.
+//
+// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/query-tables
+func (t *TableServiceClient) QueryTables(ml MetadataLevel, options *QueryTablesOptions) (*TableQueryResult, error) {
+ query, headers := options.getParameters()
+ uri := t.client.getEndpoint(tableServiceName, tablesURIPath, query)
+ return t.queryTables(uri, headers, ml)
+}
+
+// NextResults returns the next page of results
+// from a QueryTables or a NextResults operation.
+//
+// See https://docs.microsoft.com/rest/api/storageservices/fileservices/query-tables
+// See https://docs.microsoft.com/rest/api/storageservices/fileservices/query-timeout-and-pagination
+func (tqr *TableQueryResult) NextResults(options *TableOptions) (*TableQueryResult, error) {
+ if tqr == nil {
+ return nil, errNilPreviousResult
+ }
+ if tqr.NextLink == nil {
+ return nil, errNilNextLink
+ }
+ headers := options.addToHeaders(map[string]string{})
+
+ return tqr.tsc.queryTables(*tqr.NextLink, headers, tqr.ml)
+}
+
+// TableQueryResult contains the response from
+// QueryTables and QueryTablesNextResults functions.
+type TableQueryResult struct {
+ OdataMetadata string `json:"odata.metadata"`
+ Tables []Table `json:"value"`
+ QueryNextLink
+ tsc *TableServiceClient
+}
+
+func (t *TableServiceClient) queryTables(uri string, headers map[string]string, ml MetadataLevel) (*TableQueryResult, error) {
+ if ml == EmptyPayload {
+ return nil, errEmptyPayload
+ }
+ headers = mergeHeaders(headers, t.client.getStandardHeaders())
+ headers[headerAccept] = string(ml)
+
+ resp, err := t.client.exec(http.MethodGet, uri, headers, nil, t.auth)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.body.Close()
+
+ if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
+ return nil, err
+ }
+
+ respBody, err := ioutil.ReadAll(resp.body)
+ if err != nil {
+ return nil, err
+ }
+ var out TableQueryResult
+ err = json.Unmarshal(respBody, &out)
+ if err != nil {
+ return nil, err
+ }
+
+ for i := range out.Tables {
+ out.Tables[i].tsc = t
+ }
+ out.tsc = t
+
+ nextLink := resp.headers.Get(http.CanonicalHeaderKey(headerXmsContinuation))
+ if nextLink == "" {
+ out.NextLink = nil
+ } else {
+ originalURI, err := url.Parse(uri)
+ if err != nil {
+ return nil, err
+ }
+ v := originalURI.Query()
+ v.Set(nextTableQueryParameter, nextLink)
+ newURI := t.client.getEndpoint(tableServiceName, tablesURIPath, v)
+ out.NextLink = &newURI
+ out.ml = ml
+ }
+
+ return &out, nil
+}
+
+func addBodyRelatedHeaders(h map[string]string, length int) map[string]string {
+ h[headerContentType] = "application/json"
+ h[headerContentLength] = fmt.Sprintf("%v", length)
+ h[headerAcceptCharset] = "UTF-8"
+ return h
+}
+
+func addReturnContentHeaders(h map[string]string, ml MetadataLevel) map[string]string {
+ if ml != EmptyPayload {
+ h[headerPrefer] = "return-content"
+ h[headerAccept] = string(ml)
+ } else {
+ h[headerPrefer] = "return-no-content"
+ // From API version 2015-12-11 onwards, Accept header is required
+ h[headerAccept] = string(NoMetadata)
+ }
+ return h
+}
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/util.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/util.go
new file mode 100644
index 000000000..8a902be2f
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/util.go
@@ -0,0 +1,199 @@
+package storage
+
+import (
+ "bytes"
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/base64"
+ "encoding/xml"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+var (
+ fixedTime = time.Date(2050, time.December, 20, 21, 55, 0, 0, time.FixedZone("GMT", -6))
+)
+
+func (c Client) computeHmac256(message string) string {
+ h := hmac.New(sha256.New, c.accountKey)
+ h.Write([]byte(message))
+ return base64.StdEncoding.EncodeToString(h.Sum(nil))
+}
+
+func currentTimeRfc1123Formatted() string {
+ return timeRfc1123Formatted(time.Now().UTC())
+}
+
+func timeRfc1123Formatted(t time.Time) string {
+ return t.Format(http.TimeFormat)
+}
+
+func mergeParams(v1, v2 url.Values) url.Values {
+ out := url.Values{}
+ for k, v := range v1 {
+ out[k] = v
+ }
+ for k, v := range v2 {
+ vals, ok := out[k]
+ if ok {
+ vals = append(vals, v...)
+ out[k] = vals
+ } else {
+ out[k] = v
+ }
+ }
+ return out
+}
+
+func prepareBlockListRequest(blocks []Block) string {
+ s := ``
+ for _, v := range blocks {
+ s += fmt.Sprintf("<%s>%s%s>", v.Status, v.ID, v.Status)
+ }
+ s += ``
+ return s
+}
+
+func xmlUnmarshal(body io.Reader, v interface{}) error {
+ data, err := ioutil.ReadAll(body)
+ if err != nil {
+ return err
+ }
+ return xml.Unmarshal(data, v)
+}
+
+func xmlMarshal(v interface{}) (io.Reader, int, error) {
+ b, err := xml.Marshal(v)
+ if err != nil {
+ return nil, 0, err
+ }
+ return bytes.NewReader(b), len(b), nil
+}
+
+func headersFromStruct(v interface{}) map[string]string {
+ headers := make(map[string]string)
+ value := reflect.ValueOf(v)
+ for i := 0; i < value.NumField(); i++ {
+ key := value.Type().Field(i).Tag.Get("header")
+ if key != "" {
+ reflectedValue := reflect.Indirect(value.Field(i))
+ var val string
+ if reflectedValue.IsValid() {
+ switch reflectedValue.Type() {
+ case reflect.TypeOf(fixedTime):
+ val = timeRfc1123Formatted(reflectedValue.Interface().(time.Time))
+ case reflect.TypeOf(uint64(0)), reflect.TypeOf(uint(0)):
+ val = strconv.FormatUint(reflectedValue.Uint(), 10)
+ case reflect.TypeOf(int(0)):
+ val = strconv.FormatInt(reflectedValue.Int(), 10)
+ default:
+ val = reflectedValue.String()
+ }
+ }
+ if val != "" {
+ headers[key] = val
+ }
+ }
+ }
+ return headers
+}
+
+// merges extraHeaders into headers and returns headers
+func mergeHeaders(headers, extraHeaders map[string]string) map[string]string {
+ for k, v := range extraHeaders {
+ headers[k] = v
+ }
+ return headers
+}
+
+func addToHeaders(h map[string]string, key, value string) map[string]string {
+ if value != "" {
+ h[key] = value
+ }
+ return h
+}
+
+func addTimeToHeaders(h map[string]string, key string, value *time.Time) map[string]string {
+ if value != nil {
+ h = addToHeaders(h, key, timeRfc1123Formatted(*value))
+ }
+ return h
+}
+
+func addTimeout(params url.Values, timeout uint) url.Values {
+ if timeout > 0 {
+ params.Add("timeout", fmt.Sprintf("%v", timeout))
+ }
+ return params
+}
+
+func addSnapshot(params url.Values, snapshot *time.Time) url.Values {
+ if snapshot != nil {
+ params.Add("snapshot", timeRfc1123Formatted(*snapshot))
+ }
+ return params
+}
+
+func getTimeFromHeaders(h http.Header, key string) (*time.Time, error) {
+ var out time.Time
+ var err error
+ outStr := h.Get(key)
+ if outStr != "" {
+ out, err = time.Parse(time.RFC1123, outStr)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return &out, nil
+}
+
+// TimeRFC1123 is an alias for time.Time needed for custom Unmarshalling
+type TimeRFC1123 time.Time
+
+// UnmarshalXML is a custom unmarshaller that overrides the default time unmarshal which uses a different time layout.
+func (t *TimeRFC1123) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ var value string
+ d.DecodeElement(&value, &start)
+ parse, err := time.Parse(time.RFC1123, value)
+ if err != nil {
+ return err
+ }
+ *t = TimeRFC1123(parse)
+ return nil
+}
+
+// returns a map of custom metadata values from the specified HTTP header
+func getMetadataFromHeaders(header http.Header) map[string]string {
+ metadata := make(map[string]string)
+ for k, v := range header {
+ // Can't trust CanonicalHeaderKey() to munge case
+ // reliably. "_" is allowed in identifiers:
+ // https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx
+ // https://msdn.microsoft.com/library/aa664670(VS.71).aspx
+ // http://tools.ietf.org/html/rfc7230#section-3.2
+ // ...but "_" is considered invalid by
+ // CanonicalMIMEHeaderKey in
+ // https://golang.org/src/net/textproto/reader.go?s=14615:14659#L542
+ // so k can be "X-Ms-Meta-Lol" or "x-ms-meta-lol_rofl".
+ k = strings.ToLower(k)
+ if len(v) == 0 || !strings.HasPrefix(k, strings.ToLower(userDefinedMetadataHeaderPrefix)) {
+ continue
+ }
+ // metadata["lol"] = content of the last X-Ms-Meta-Lol header
+ k = k[len(userDefinedMetadataHeaderPrefix):]
+ metadata[k] = v[len(v)-1]
+ }
+
+ if len(metadata) == 0 {
+ return nil
+ }
+
+ return metadata
+}
diff --git a/vendor/github.com/Azure/azure-storage-go/version.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/version.go
similarity index 52%
rename from vendor/github.com/Azure/azure-storage-go/version.go
rename to vendor/github.com/Azure/azure-sdk-for-go/storage/version.go
index c25fe3371..a23fff1e2 100644
--- a/vendor/github.com/Azure/azure-storage-go/version.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/storage/version.go
@@ -1,5 +1,5 @@
package storage
var (
- sdkVersion = "0.1.0"
+ sdkVersion = "10.0.2"
)
diff --git a/vendor/github.com/Azure/azure-storage-go/LICENSE b/vendor/github.com/Azure/azure-storage-go/LICENSE
deleted file mode 100644
index 4b1ad51b2..000000000
--- a/vendor/github.com/Azure/azure-storage-go/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
- MIT License
-
- Copyright (c) Microsoft Corporation. All rights reserved.
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE
diff --git a/vendor/github.com/Azure/azure-storage-go/README.md b/vendor/github.com/Azure/azure-storage-go/README.md
deleted file mode 100644
index f4af7bf39..000000000
--- a/vendor/github.com/Azure/azure-storage-go/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# Azure Storage SDK for Go
-[![GoDoc](https://godoc.org/github.com/Azure/azure-storage-go?status.svg)](https://godoc.org/github.com/Azure/azure-storage-go) [![Build Status](https://travis-ci.org/Azure/azure-storage-go.svg?branch=master)](https://travis-ci.org/Azure/azure-storage-go) [![Go Report Card](https://goreportcard.com/badge/github.com/Azure/azure-storage-go)](https://goreportcard.com/report/github.com/Azure/azure-storage-go)
-
-The `github.com/Azure/azure-sdk-for-go/storage` package is used to perform operations in Azure Storage Service. To manage your storage accounts (Azure Resource Manager / ARM), use the [github.com/Azure/azure-sdk-for-go/arm/storage](../arm/storage) package. For your classic storage accounts (Azure Service Management / ASM), use [github.com/Azure/azure-sdk-for-go/management/storageservice](../management/storageservice) package.
-
-This package includes support for [Azure Storage Emulator](https://azure.microsoft.com/documentation/articles/storage-use-emulator/)
-
-# Contributing
-
-This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
diff --git a/vendor/github.com/Azure/azure-storage-go/blob.go b/vendor/github.com/Azure/azure-storage-go/blob.go
deleted file mode 100644
index 636efc662..000000000
--- a/vendor/github.com/Azure/azure-storage-go/blob.go
+++ /dev/null
@@ -1,1130 +0,0 @@
-package storage
-
-import (
- "bytes"
- "encoding/xml"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "strconv"
- "strings"
- "time"
-)
-
-// A Blob is an entry in BlobListResponse.
-type Blob struct {
- Name string `xml:"Name"`
- Properties BlobProperties `xml:"Properties"`
- Metadata BlobMetadata `xml:"Metadata"`
-}
-
-// BlobMetadata is a set of custom name/value pairs.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179404.aspx
-type BlobMetadata map[string]string
-
-type blobMetadataEntries struct {
- Entries []blobMetadataEntry `xml:",any"`
-}
-type blobMetadataEntry struct {
- XMLName xml.Name
- Value string `xml:",chardata"`
-}
-
-// UnmarshalXML converts the xml:Metadata into Metadata map
-func (bm *BlobMetadata) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
- var entries blobMetadataEntries
- if err := d.DecodeElement(&entries, &start); err != nil {
- return err
- }
- for _, entry := range entries.Entries {
- if *bm == nil {
- *bm = make(BlobMetadata)
- }
- (*bm)[strings.ToLower(entry.XMLName.Local)] = entry.Value
- }
- return nil
-}
-
-// MarshalXML implements the xml.Marshaler interface. It encodes
-// metadata name/value pairs as they would appear in an Azure
-// ListBlobs response.
-func (bm BlobMetadata) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {
- entries := make([]blobMetadataEntry, 0, len(bm))
- for k, v := range bm {
- entries = append(entries, blobMetadataEntry{
- XMLName: xml.Name{Local: http.CanonicalHeaderKey(k)},
- Value: v,
- })
- }
- return enc.EncodeElement(blobMetadataEntries{
- Entries: entries,
- }, start)
-}
-
-// BlobProperties contains various properties of a blob
-// returned in various endpoints like ListBlobs or GetBlobProperties.
-type BlobProperties struct {
- LastModified string `xml:"Last-Modified"`
- Etag string `xml:"Etag"`
- ContentMD5 string `xml:"Content-MD5"`
- ContentLength int64 `xml:"Content-Length"`
- ContentType string `xml:"Content-Type"`
- ContentEncoding string `xml:"Content-Encoding"`
- CacheControl string `xml:"Cache-Control"`
- ContentLanguage string `xml:"Cache-Language"`
- BlobType BlobType `xml:"x-ms-blob-blob-type"`
- SequenceNumber int64 `xml:"x-ms-blob-sequence-number"`
- CopyID string `xml:"CopyId"`
- CopyStatus string `xml:"CopyStatus"`
- CopySource string `xml:"CopySource"`
- CopyProgress string `xml:"CopyProgress"`
- CopyCompletionTime string `xml:"CopyCompletionTime"`
- CopyStatusDescription string `xml:"CopyStatusDescription"`
- LeaseStatus string `xml:"LeaseStatus"`
- LeaseState string `xml:"LeaseState"`
-}
-
-// BlobHeaders contains various properties of a blob and is an entry
-// in SetBlobProperties
-type BlobHeaders struct {
- ContentMD5 string `header:"x-ms-blob-content-md5"`
- ContentLanguage string `header:"x-ms-blob-content-language"`
- ContentEncoding string `header:"x-ms-blob-content-encoding"`
- ContentType string `header:"x-ms-blob-content-type"`
- CacheControl string `header:"x-ms-blob-cache-control"`
-}
-
-// BlobType defines the type of the Azure Blob.
-type BlobType string
-
-// Types of page blobs
-const (
- BlobTypeBlock BlobType = "BlockBlob"
- BlobTypePage BlobType = "PageBlob"
- BlobTypeAppend BlobType = "AppendBlob"
-)
-
-// PageWriteType defines the type updates that are going to be
-// done on the page blob.
-type PageWriteType string
-
-// Types of operations on page blobs
-const (
- PageWriteTypeUpdate PageWriteType = "update"
- PageWriteTypeClear PageWriteType = "clear"
-)
-
-const (
- blobCopyStatusPending = "pending"
- blobCopyStatusSuccess = "success"
- blobCopyStatusAborted = "aborted"
- blobCopyStatusFailed = "failed"
-)
-
-// lease constants.
-const (
- leaseHeaderPrefix = "x-ms-lease-"
- headerLeaseID = "x-ms-lease-id"
- leaseAction = "x-ms-lease-action"
- leaseBreakPeriod = "x-ms-lease-break-period"
- leaseDuration = "x-ms-lease-duration"
- leaseProposedID = "x-ms-proposed-lease-id"
- leaseTime = "x-ms-lease-time"
-
- acquireLease = "acquire"
- renewLease = "renew"
- changeLease = "change"
- releaseLease = "release"
- breakLease = "break"
-)
-
-// BlockListType is used to filter out types of blocks in a Get Blocks List call
-// for a block blob.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179400.aspx for all
-// block types.
-type BlockListType string
-
-// Filters for listing blocks in block blobs
-const (
- BlockListTypeAll BlockListType = "all"
- BlockListTypeCommitted BlockListType = "committed"
- BlockListTypeUncommitted BlockListType = "uncommitted"
-)
-
-// Maximum sizes (per REST API) for various concepts
-const (
- MaxBlobBlockSize = 100 * 1024 * 1024
- MaxBlobPageSize = 4 * 1024 * 1024
-)
-
-// BlockStatus defines states a block for a block blob can
-// be in.
-type BlockStatus string
-
-// List of statuses that can be used to refer to a block in a block list
-const (
- BlockStatusUncommitted BlockStatus = "Uncommitted"
- BlockStatusCommitted BlockStatus = "Committed"
- BlockStatusLatest BlockStatus = "Latest"
-)
-
-// Block is used to create Block entities for Put Block List
-// call.
-type Block struct {
- ID string
- Status BlockStatus
-}
-
-// BlockListResponse contains the response fields from Get Block List call.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179400.aspx
-type BlockListResponse struct {
- XMLName xml.Name `xml:"BlockList"`
- CommittedBlocks []BlockResponse `xml:"CommittedBlocks>Block"`
- UncommittedBlocks []BlockResponse `xml:"UncommittedBlocks>Block"`
-}
-
-// BlockResponse contains the block information returned
-// in the GetBlockListCall.
-type BlockResponse struct {
- Name string `xml:"Name"`
- Size int64 `xml:"Size"`
-}
-
-// GetPageRangesResponse contains the response fields from
-// Get Page Ranges call.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/ee691973.aspx
-type GetPageRangesResponse struct {
- XMLName xml.Name `xml:"PageList"`
- PageList []PageRange `xml:"PageRange"`
-}
-
-// PageRange contains information about a page of a page blob from
-// Get Pages Range call.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/ee691973.aspx
-type PageRange struct {
- Start int64 `xml:"Start"`
- End int64 `xml:"End"`
-}
-
-var (
- errBlobCopyAborted = errors.New("storage: blob copy is aborted")
- errBlobCopyIDMismatch = errors.New("storage: blob copy id is a mismatch")
-)
-
-// BlobExists returns true if a blob with given name exists on the specified
-// container of the storage account.
-func (b BlobStorageClient) BlobExists(container, name string) (bool, error) {
- uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{})
- headers := b.client.getStandardHeaders()
- resp, err := b.client.exec(http.MethodHead, uri, headers, nil, b.auth)
- if resp != nil {
- defer readAndCloseBody(resp.body)
- if resp.statusCode == http.StatusOK || resp.statusCode == http.StatusNotFound {
- return resp.statusCode == http.StatusOK, nil
- }
- }
- return false, err
-}
-
-// GetBlobURL gets the canonical URL to the blob with the specified name in the
-// specified container. If name is not specified, the canonical URL for the entire
-// container is obtained.
-// This method does not create a publicly accessible URL if the blob or container
-// is private and this method does not check if the blob exists.
-func (b BlobStorageClient) GetBlobURL(container, name string) string {
- if container == "" {
- container = "$root"
- }
- return b.client.getEndpoint(blobServiceName, pathForResource(container, name), url.Values{})
-}
-
-// GetBlob returns a stream to read the blob. Caller must call Close() the
-// reader to close on the underlying connection.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179440.aspx
-func (b BlobStorageClient) GetBlob(container, name string) (io.ReadCloser, error) {
- resp, err := b.getBlobRange(container, name, "", nil)
- if err != nil {
- return nil, err
- }
-
- if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
- return nil, err
- }
- return resp.body, nil
-}
-
-// GetBlobRange reads the specified range of a blob to a stream. The bytesRange
-// string must be in a format like "0-", "10-100" as defined in HTTP 1.1 spec.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179440.aspx
-func (b BlobStorageClient) GetBlobRange(container, name, bytesRange string, extraHeaders map[string]string) (io.ReadCloser, error) {
- resp, err := b.getBlobRange(container, name, bytesRange, extraHeaders)
- if err != nil {
- return nil, err
- }
-
- if err := checkRespCode(resp.statusCode, []int{http.StatusPartialContent}); err != nil {
- return nil, err
- }
- return resp.body, nil
-}
-
-func (b BlobStorageClient) getBlobRange(container, name, bytesRange string, extraHeaders map[string]string) (*storageResponse, error) {
- uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{})
-
- extraHeaders = b.client.protectUserAgent(extraHeaders)
- headers := b.client.getStandardHeaders()
- if bytesRange != "" {
- headers["Range"] = fmt.Sprintf("bytes=%s", bytesRange)
- }
-
- for k, v := range extraHeaders {
- headers[k] = v
- }
-
- resp, err := b.client.exec(http.MethodGet, uri, headers, nil, b.auth)
- if err != nil {
- return nil, err
- }
- return resp, err
-}
-
-// leasePut is common PUT code for the various acquire/release/break etc functions.
-func (b BlobStorageClient) leaseCommonPut(container string, name string, headers map[string]string, expectedStatus int) (http.Header, error) {
- params := url.Values{"comp": {"lease"}}
- uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params)
-
- resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth)
- if err != nil {
- return nil, err
- }
- defer readAndCloseBody(resp.body)
-
- if err := checkRespCode(resp.statusCode, []int{expectedStatus}); err != nil {
- return nil, err
- }
-
- return resp.headers, nil
-}
-
-// SnapshotBlob creates a snapshot for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691971.aspx
-func (b BlobStorageClient) SnapshotBlob(container string, name string, timeout int, extraHeaders map[string]string) (snapshotTimestamp *time.Time, err error) {
- extraHeaders = b.client.protectUserAgent(extraHeaders)
- headers := b.client.getStandardHeaders()
- params := url.Values{"comp": {"snapshot"}}
-
- if timeout > 0 {
- params.Add("timeout", strconv.Itoa(timeout))
- }
-
- for k, v := range extraHeaders {
- headers[k] = v
- }
-
- uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params)
- resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth)
- if err != nil || resp == nil {
- return nil, err
- }
-
- defer readAndCloseBody(resp.body)
-
- if err := checkRespCode(resp.statusCode, []int{http.StatusCreated}); err != nil {
- return nil, err
- }
-
- snapshotResponse := resp.headers.Get(http.CanonicalHeaderKey("x-ms-snapshot"))
- if snapshotResponse != "" {
- snapshotTimestamp, err := time.Parse(time.RFC3339, snapshotResponse)
- if err != nil {
- return nil, err
- }
-
- return &snapshotTimestamp, nil
- }
-
- return nil, errors.New("Snapshot not created")
-}
-
-// AcquireLease creates a lease for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
-// returns leaseID acquired
-// In API Versions starting on 2012-02-12, the minimum leaseTimeInSeconds is 15, the maximum
-// non-infinite leaseTimeInSeconds is 60. To specify an infinite lease, provide the value -1.
-func (b BlobStorageClient) AcquireLease(container string, name string, leaseTimeInSeconds int, proposedLeaseID string) (returnedLeaseID string, err error) {
- headers := b.client.getStandardHeaders()
- headers[leaseAction] = acquireLease
-
- if leaseTimeInSeconds == -1 {
- // Do nothing, but don't trigger the following clauses.
- } else if leaseTimeInSeconds > 60 || b.client.apiVersion < "2012-02-12" {
- leaseTimeInSeconds = 60
- } else if leaseTimeInSeconds < 15 {
- leaseTimeInSeconds = 15
- }
-
- headers[leaseDuration] = strconv.Itoa(leaseTimeInSeconds)
-
- if proposedLeaseID != "" {
- headers[leaseProposedID] = proposedLeaseID
- }
-
- respHeaders, err := b.leaseCommonPut(container, name, headers, http.StatusCreated)
- if err != nil {
- return "", err
- }
-
- returnedLeaseID = respHeaders.Get(http.CanonicalHeaderKey(headerLeaseID))
-
- if returnedLeaseID != "" {
- return returnedLeaseID, nil
- }
-
- return "", errors.New("LeaseID not returned")
-}
-
-// BreakLease breaks the lease for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
-// Returns the timeout remaining in the lease in seconds
-func (b BlobStorageClient) BreakLease(container string, name string) (breakTimeout int, err error) {
- headers := b.client.getStandardHeaders()
- headers[leaseAction] = breakLease
- return b.breakLeaseCommon(container, name, headers)
-}
-
-// BreakLeaseWithBreakPeriod breaks the lease for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
-// breakPeriodInSeconds is used to determine how long until new lease can be created.
-// Returns the timeout remaining in the lease in seconds
-func (b BlobStorageClient) BreakLeaseWithBreakPeriod(container string, name string, breakPeriodInSeconds int) (breakTimeout int, err error) {
- headers := b.client.getStandardHeaders()
- headers[leaseAction] = breakLease
- headers[leaseBreakPeriod] = strconv.Itoa(breakPeriodInSeconds)
- return b.breakLeaseCommon(container, name, headers)
-}
-
-// breakLeaseCommon is common code for both version of BreakLease (with and without break period)
-func (b BlobStorageClient) breakLeaseCommon(container string, name string, headers map[string]string) (breakTimeout int, err error) {
-
- respHeaders, err := b.leaseCommonPut(container, name, headers, http.StatusAccepted)
- if err != nil {
- return 0, err
- }
-
- breakTimeoutStr := respHeaders.Get(http.CanonicalHeaderKey(leaseTime))
- if breakTimeoutStr != "" {
- breakTimeout, err = strconv.Atoi(breakTimeoutStr)
- if err != nil {
- return 0, err
- }
- }
-
- return breakTimeout, nil
-}
-
-// ChangeLease changes a lease ID for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
-// Returns the new LeaseID acquired
-func (b BlobStorageClient) ChangeLease(container string, name string, currentLeaseID string, proposedLeaseID string) (newLeaseID string, err error) {
- headers := b.client.getStandardHeaders()
- headers[leaseAction] = changeLease
- headers[headerLeaseID] = currentLeaseID
- headers[leaseProposedID] = proposedLeaseID
-
- respHeaders, err := b.leaseCommonPut(container, name, headers, http.StatusOK)
- if err != nil {
- return "", err
- }
-
- newLeaseID = respHeaders.Get(http.CanonicalHeaderKey(headerLeaseID))
- if newLeaseID != "" {
- return newLeaseID, nil
- }
-
- return "", errors.New("LeaseID not returned")
-}
-
-// ReleaseLease releases the lease for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
-func (b BlobStorageClient) ReleaseLease(container string, name string, currentLeaseID string) error {
- headers := b.client.getStandardHeaders()
- headers[leaseAction] = releaseLease
- headers[headerLeaseID] = currentLeaseID
-
- _, err := b.leaseCommonPut(container, name, headers, http.StatusOK)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// RenewLease renews the lease for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
-func (b BlobStorageClient) RenewLease(container string, name string, currentLeaseID string) error {
- headers := b.client.getStandardHeaders()
- headers[leaseAction] = renewLease
- headers[headerLeaseID] = currentLeaseID
-
- _, err := b.leaseCommonPut(container, name, headers, http.StatusOK)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// GetBlobProperties provides various information about the specified
-// blob. See https://msdn.microsoft.com/en-us/library/azure/dd179394.aspx
-func (b BlobStorageClient) GetBlobProperties(container, name string) (*BlobProperties, error) {
- uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{})
-
- headers := b.client.getStandardHeaders()
- resp, err := b.client.exec(http.MethodHead, uri, headers, nil, b.auth)
- if err != nil {
- return nil, err
- }
- defer readAndCloseBody(resp.body)
-
- if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
- return nil, err
- }
-
- var contentLength int64
- contentLengthStr := resp.headers.Get("Content-Length")
- if contentLengthStr != "" {
- contentLength, err = strconv.ParseInt(contentLengthStr, 0, 64)
- if err != nil {
- return nil, err
- }
- }
-
- var sequenceNum int64
- sequenceNumStr := resp.headers.Get("x-ms-blob-sequence-number")
- if sequenceNumStr != "" {
- sequenceNum, err = strconv.ParseInt(sequenceNumStr, 0, 64)
- if err != nil {
- return nil, err
- }
- }
-
- return &BlobProperties{
- LastModified: resp.headers.Get("Last-Modified"),
- Etag: resp.headers.Get("Etag"),
- ContentMD5: resp.headers.Get("Content-MD5"),
- ContentLength: contentLength,
- ContentEncoding: resp.headers.Get("Content-Encoding"),
- ContentType: resp.headers.Get("Content-Type"),
- CacheControl: resp.headers.Get("Cache-Control"),
- ContentLanguage: resp.headers.Get("Content-Language"),
- SequenceNumber: sequenceNum,
- CopyCompletionTime: resp.headers.Get("x-ms-copy-completion-time"),
- CopyStatusDescription: resp.headers.Get("x-ms-copy-status-description"),
- CopyID: resp.headers.Get("x-ms-copy-id"),
- CopyProgress: resp.headers.Get("x-ms-copy-progress"),
- CopySource: resp.headers.Get("x-ms-copy-source"),
- CopyStatus: resp.headers.Get("x-ms-copy-status"),
- BlobType: BlobType(resp.headers.Get("x-ms-blob-type")),
- LeaseStatus: resp.headers.Get("x-ms-lease-status"),
- LeaseState: resp.headers.Get("x-ms-lease-state"),
- }, nil
-}
-
-// SetBlobProperties replaces the BlobHeaders for the specified blob.
-//
-// Some keys may be converted to Camel-Case before sending. All keys
-// are returned in lower case by GetBlobProperties. HTTP header names
-// are case-insensitive so case munging should not matter to other
-// applications either.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/ee691966.aspx
-func (b BlobStorageClient) SetBlobProperties(container, name string, blobHeaders BlobHeaders) error {
- params := url.Values{"comp": {"properties"}}
- uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params)
- headers := b.client.getStandardHeaders()
-
- extraHeaders := headersFromStruct(blobHeaders)
-
- for k, v := range extraHeaders {
- headers[k] = v
- }
-
- resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
-
- return checkRespCode(resp.statusCode, []int{http.StatusOK})
-}
-
-// SetBlobMetadata replaces the metadata for the specified blob.
-//
-// Some keys may be converted to Camel-Case before sending. All keys
-// are returned in lower case by GetBlobMetadata. HTTP header names
-// are case-insensitive so case munging should not matter to other
-// applications either.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx
-func (b BlobStorageClient) SetBlobMetadata(container, name string, metadata map[string]string, extraHeaders map[string]string) error {
- params := url.Values{"comp": {"metadata"}}
- uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params)
- metadata = b.client.protectUserAgent(metadata)
- extraHeaders = b.client.protectUserAgent(extraHeaders)
- headers := b.client.getStandardHeaders()
- for k, v := range metadata {
- headers[userDefinedMetadataHeaderPrefix+k] = v
- }
-
- for k, v := range extraHeaders {
- headers[k] = v
- }
-
- resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
-
- return checkRespCode(resp.statusCode, []int{http.StatusOK})
-}
-
-// GetBlobMetadata returns all user-defined metadata for the specified blob.
-//
-// All metadata keys will be returned in lower case. (HTTP header
-// names are case-insensitive.)
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx
-func (b BlobStorageClient) GetBlobMetadata(container, name string) (map[string]string, error) {
- params := url.Values{"comp": {"metadata"}}
- uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params)
- headers := b.client.getStandardHeaders()
-
- resp, err := b.client.exec(http.MethodGet, uri, headers, nil, b.auth)
- if err != nil {
- return nil, err
- }
- defer readAndCloseBody(resp.body)
-
- if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
- return nil, err
- }
-
- metadata := make(map[string]string)
- for k, v := range resp.headers {
- // Can't trust CanonicalHeaderKey() to munge case
- // reliably. "_" is allowed in identifiers:
- // https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx
- // https://msdn.microsoft.com/library/aa664670(VS.71).aspx
- // http://tools.ietf.org/html/rfc7230#section-3.2
- // ...but "_" is considered invalid by
- // CanonicalMIMEHeaderKey in
- // https://golang.org/src/net/textproto/reader.go?s=14615:14659#L542
- // so k can be "X-Ms-Meta-Foo" or "x-ms-meta-foo_bar".
- k = strings.ToLower(k)
- if len(v) == 0 || !strings.HasPrefix(k, strings.ToLower(userDefinedMetadataHeaderPrefix)) {
- continue
- }
- // metadata["foo"] = content of the last X-Ms-Meta-Foo header
- k = k[len(userDefinedMetadataHeaderPrefix):]
- metadata[k] = v[len(v)-1]
- }
- return metadata, nil
-}
-
-// CreateBlockBlob initializes an empty block blob with no blocks.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179451.aspx
-func (b BlobStorageClient) CreateBlockBlob(container, name string) error {
- return b.CreateBlockBlobFromReader(container, name, 0, nil, nil)
-}
-
-// CreateBlockBlobFromReader initializes a block blob using data from
-// reader. Size must be the number of bytes read from reader. To
-// create an empty blob, use size==0 and reader==nil.
-//
-// The API rejects requests with size > 256 MiB (but this limit is not
-// checked by the SDK). To write a larger blob, use CreateBlockBlob,
-// PutBlock, and PutBlockList.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179451.aspx
-func (b BlobStorageClient) CreateBlockBlobFromReader(container, name string, size uint64, blob io.Reader, extraHeaders map[string]string) error {
- path := fmt.Sprintf("%s/%s", container, name)
- uri := b.client.getEndpoint(blobServiceName, path, url.Values{})
- extraHeaders = b.client.protectUserAgent(extraHeaders)
- headers := b.client.getStandardHeaders()
- headers["x-ms-blob-type"] = string(BlobTypeBlock)
- headers["Content-Length"] = fmt.Sprintf("%d", size)
-
- for k, v := range extraHeaders {
- headers[k] = v
- }
-
- resp, err := b.client.exec(http.MethodPut, uri, headers, blob, b.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
- return checkRespCode(resp.statusCode, []int{http.StatusCreated})
-}
-
-// PutBlock saves the given data chunk to the specified block blob with
-// given ID.
-//
-// The API rejects chunks larger than 100 MB (but this limit is not
-// checked by the SDK).
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd135726.aspx
-func (b BlobStorageClient) PutBlock(container, name, blockID string, chunk []byte) error {
- return b.PutBlockWithLength(container, name, blockID, uint64(len(chunk)), bytes.NewReader(chunk), nil)
-}
-
-// PutBlockWithLength saves the given data stream of exactly specified size to
-// the block blob with given ID. It is an alternative to PutBlocks where data
-// comes as stream but the length is known in advance.
-//
-// The API rejects requests with size > 100 MB (but this limit is not
-// checked by the SDK).
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd135726.aspx
-func (b BlobStorageClient) PutBlockWithLength(container, name, blockID string, size uint64, blob io.Reader, extraHeaders map[string]string) error {
- uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{"comp": {"block"}, "blockid": {blockID}})
- extraHeaders = b.client.protectUserAgent(extraHeaders)
- headers := b.client.getStandardHeaders()
- headers["x-ms-blob-type"] = string(BlobTypeBlock)
- headers["Content-Length"] = fmt.Sprintf("%v", size)
-
- for k, v := range extraHeaders {
- headers[k] = v
- }
-
- resp, err := b.client.exec(http.MethodPut, uri, headers, blob, b.auth)
- if err != nil {
- return err
- }
-
- defer readAndCloseBody(resp.body)
- return checkRespCode(resp.statusCode, []int{http.StatusCreated})
-}
-
-// PutBlockList saves list of blocks to the specified block blob.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179467.aspx
-func (b BlobStorageClient) PutBlockList(container, name string, blocks []Block) error {
- blockListXML := prepareBlockListRequest(blocks)
-
- uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{"comp": {"blocklist"}})
- headers := b.client.getStandardHeaders()
- headers["Content-Length"] = fmt.Sprintf("%v", len(blockListXML))
-
- resp, err := b.client.exec(http.MethodPut, uri, headers, strings.NewReader(blockListXML), b.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
- return checkRespCode(resp.statusCode, []int{http.StatusCreated})
-}
-
-// GetBlockList retrieves list of blocks in the specified block blob.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179400.aspx
-func (b BlobStorageClient) GetBlockList(container, name string, blockType BlockListType) (BlockListResponse, error) {
- params := url.Values{"comp": {"blocklist"}, "blocklisttype": {string(blockType)}}
- uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params)
- headers := b.client.getStandardHeaders()
-
- var out BlockListResponse
- resp, err := b.client.exec(http.MethodGet, uri, headers, nil, b.auth)
- if err != nil {
- return out, err
- }
- defer resp.body.Close()
-
- err = xmlUnmarshal(resp.body, &out)
- return out, err
-}
-
-// PutPageBlob initializes an empty page blob with specified name and maximum
-// size in bytes (size must be aligned to a 512-byte boundary). A page blob must
-// be created using this method before writing pages.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179451.aspx
-func (b BlobStorageClient) PutPageBlob(container, name string, size int64, extraHeaders map[string]string) error {
- path := fmt.Sprintf("%s/%s", container, name)
- uri := b.client.getEndpoint(blobServiceName, path, url.Values{})
- extraHeaders = b.client.protectUserAgent(extraHeaders)
- headers := b.client.getStandardHeaders()
- headers["x-ms-blob-type"] = string(BlobTypePage)
- headers["x-ms-blob-content-length"] = fmt.Sprintf("%v", size)
-
- for k, v := range extraHeaders {
- headers[k] = v
- }
-
- resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
-
- return checkRespCode(resp.statusCode, []int{http.StatusCreated})
-}
-
-// PutPage writes a range of pages to a page blob or clears the given range.
-// In case of 'clear' writes, given chunk is discarded. Ranges must be aligned
-// with 512-byte boundaries and chunk must be of size multiplies by 512.
-//
-// See https://msdn.microsoft.com/en-us/library/ee691975.aspx
-func (b BlobStorageClient) PutPage(container, name string, startByte, endByte int64, writeType PageWriteType, chunk []byte, extraHeaders map[string]string) error {
- path := fmt.Sprintf("%s/%s", container, name)
- uri := b.client.getEndpoint(blobServiceName, path, url.Values{"comp": {"page"}})
- extraHeaders = b.client.protectUserAgent(extraHeaders)
- headers := b.client.getStandardHeaders()
- headers["x-ms-blob-type"] = string(BlobTypePage)
- headers["x-ms-page-write"] = string(writeType)
- headers["x-ms-range"] = fmt.Sprintf("bytes=%v-%v", startByte, endByte)
- for k, v := range extraHeaders {
- headers[k] = v
- }
- var contentLength int64
- var data io.Reader
- if writeType == PageWriteTypeClear {
- contentLength = 0
- data = bytes.NewReader([]byte{})
- } else {
- contentLength = int64(len(chunk))
- data = bytes.NewReader(chunk)
- }
- headers["Content-Length"] = fmt.Sprintf("%v", contentLength)
-
- resp, err := b.client.exec(http.MethodPut, uri, headers, data, b.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
-
- return checkRespCode(resp.statusCode, []int{http.StatusCreated})
-}
-
-// GetPageRanges returns the list of valid page ranges for a page blob.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/ee691973.aspx
-func (b BlobStorageClient) GetPageRanges(container, name string) (GetPageRangesResponse, error) {
- path := fmt.Sprintf("%s/%s", container, name)
- uri := b.client.getEndpoint(blobServiceName, path, url.Values{"comp": {"pagelist"}})
- headers := b.client.getStandardHeaders()
-
- var out GetPageRangesResponse
- resp, err := b.client.exec(http.MethodGet, uri, headers, nil, b.auth)
- if err != nil {
- return out, err
- }
- defer resp.body.Close()
-
- if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
- return out, err
- }
- err = xmlUnmarshal(resp.body, &out)
- return out, err
-}
-
-// PutAppendBlob initializes an empty append blob with specified name. An
-// append blob must be created using this method before appending blocks.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179451.aspx
-func (b BlobStorageClient) PutAppendBlob(container, name string, extraHeaders map[string]string) error {
- path := fmt.Sprintf("%s/%s", container, name)
- uri := b.client.getEndpoint(blobServiceName, path, url.Values{})
- extraHeaders = b.client.protectUserAgent(extraHeaders)
- headers := b.client.getStandardHeaders()
- headers["x-ms-blob-type"] = string(BlobTypeAppend)
-
- for k, v := range extraHeaders {
- headers[k] = v
- }
-
- resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
-
- return checkRespCode(resp.statusCode, []int{http.StatusCreated})
-}
-
-// AppendBlock appends a block to an append blob.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/mt427365.aspx
-func (b BlobStorageClient) AppendBlock(container, name string, chunk []byte, extraHeaders map[string]string) error {
- path := fmt.Sprintf("%s/%s", container, name)
- uri := b.client.getEndpoint(blobServiceName, path, url.Values{"comp": {"appendblock"}})
- extraHeaders = b.client.protectUserAgent(extraHeaders)
- headers := b.client.getStandardHeaders()
- headers["x-ms-blob-type"] = string(BlobTypeAppend)
- headers["Content-Length"] = fmt.Sprintf("%v", len(chunk))
-
- for k, v := range extraHeaders {
- headers[k] = v
- }
-
- resp, err := b.client.exec(http.MethodPut, uri, headers, bytes.NewReader(chunk), b.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
-
- return checkRespCode(resp.statusCode, []int{http.StatusCreated})
-}
-
-// CopyBlob starts a blob copy operation and waits for the operation to
-// complete. sourceBlob parameter must be a canonical URL to the blob (can be
-// obtained using GetBlobURL method.) There is no SLA on blob copy and therefore
-// this helper method works faster on smaller files.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd894037.aspx
-func (b BlobStorageClient) CopyBlob(container, name, sourceBlob string) error {
- copyID, err := b.StartBlobCopy(container, name, sourceBlob)
- if err != nil {
- return err
- }
-
- return b.WaitForBlobCopy(container, name, copyID)
-}
-
-// StartBlobCopy starts a blob copy operation.
-// sourceBlob parameter must be a canonical URL to the blob (can be
-// obtained using GetBlobURL method.)
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd894037.aspx
-func (b BlobStorageClient) StartBlobCopy(container, name, sourceBlob string) (string, error) {
- uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{})
-
- headers := b.client.getStandardHeaders()
- headers["x-ms-copy-source"] = sourceBlob
-
- resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth)
- if err != nil {
- return "", err
- }
- defer readAndCloseBody(resp.body)
-
- if err := checkRespCode(resp.statusCode, []int{http.StatusAccepted, http.StatusCreated}); err != nil {
- return "", err
- }
-
- copyID := resp.headers.Get("x-ms-copy-id")
- if copyID == "" {
- return "", errors.New("Got empty copy id header")
- }
- return copyID, nil
-}
-
-// AbortBlobCopy aborts a BlobCopy which has already been triggered by the StartBlobCopy function.
-// copyID is generated from StartBlobCopy function.
-// currentLeaseID is required IF the destination blob has an active lease on it.
-// As defined in https://msdn.microsoft.com/en-us/library/azure/jj159098.aspx
-func (b BlobStorageClient) AbortBlobCopy(container, name, copyID, currentLeaseID string, timeout int) error {
- params := url.Values{"comp": {"copy"}, "copyid": {copyID}}
- if timeout > 0 {
- params.Add("timeout", strconv.Itoa(timeout))
- }
-
- uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params)
- headers := b.client.getStandardHeaders()
- headers["x-ms-copy-action"] = "abort"
-
- if currentLeaseID != "" {
- headers[headerLeaseID] = currentLeaseID
- }
-
- resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
-
- if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil {
- return err
- }
-
- return nil
-}
-
-// WaitForBlobCopy loops until a BlobCopy operation is completed (or fails with error)
-func (b BlobStorageClient) WaitForBlobCopy(container, name, copyID string) error {
- for {
- props, err := b.GetBlobProperties(container, name)
- if err != nil {
- return err
- }
-
- if props.CopyID != copyID {
- return errBlobCopyIDMismatch
- }
-
- switch props.CopyStatus {
- case blobCopyStatusSuccess:
- return nil
- case blobCopyStatusPending:
- continue
- case blobCopyStatusAborted:
- return errBlobCopyAborted
- case blobCopyStatusFailed:
- return fmt.Errorf("storage: blob copy failed. Id=%s Description=%s", props.CopyID, props.CopyStatusDescription)
- default:
- return fmt.Errorf("storage: unhandled blob copy status: '%s'", props.CopyStatus)
- }
- }
-}
-
-// DeleteBlob deletes the given blob from the specified container.
-// If the blob does not exists at the time of the Delete Blob operation, it
-// returns error. See https://msdn.microsoft.com/en-us/library/azure/dd179413.aspx
-func (b BlobStorageClient) DeleteBlob(container, name string, extraHeaders map[string]string) error {
- resp, err := b.deleteBlob(container, name, extraHeaders)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
- return checkRespCode(resp.statusCode, []int{http.StatusAccepted})
-}
-
-// DeleteBlobIfExists deletes the given blob from the specified container If the
-// blob is deleted with this call, returns true. Otherwise returns false.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179413.aspx
-func (b BlobStorageClient) DeleteBlobIfExists(container, name string, extraHeaders map[string]string) (bool, error) {
- resp, err := b.deleteBlob(container, name, extraHeaders)
- if resp != nil {
- defer readAndCloseBody(resp.body)
- if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound {
- return resp.statusCode == http.StatusAccepted, nil
- }
- }
- return false, err
-}
-
-func (b BlobStorageClient) deleteBlob(container, name string, extraHeaders map[string]string) (*storageResponse, error) {
- uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{})
- extraHeaders = b.client.protectUserAgent(extraHeaders)
- headers := b.client.getStandardHeaders()
- for k, v := range extraHeaders {
- headers[k] = v
- }
-
- return b.client.exec(http.MethodDelete, uri, headers, nil, b.auth)
-}
-
-// helper method to construct the path to a blob given its container and blob
-// name
-func pathForBlob(container, name string) string {
- return fmt.Sprintf("/%s/%s", container, name)
-}
-
-// helper method to construct the path to either a blob or container
-func pathForResource(container, name string) string {
- if len(name) > 0 {
- return fmt.Sprintf("/%s/%s", container, name)
- }
- return fmt.Sprintf("/%s", container)
-}
-
-// GetBlobSASURIWithSignedIPAndProtocol creates an URL to the specified blob which contains the Shared
-// Access Signature with specified permissions and expiration time. Also includes signedIPRange and allowed protocols.
-// If old API version is used but no signedIP is passed (ie empty string) then this should still work.
-// We only populate the signedIP when it non-empty.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/ee395415.aspx
-func (b BlobStorageClient) GetBlobSASURIWithSignedIPAndProtocol(container, name string, expiry time.Time, permissions string, signedIPRange string, HTTPSOnly bool) (string, error) {
- var (
- signedPermissions = permissions
- blobURL = b.GetBlobURL(container, name)
- )
- canonicalizedResource, err := b.client.buildCanonicalizedResource(blobURL, b.auth)
- if err != nil {
- return "", err
- }
-
- // "The canonicalizedresouce portion of the string is a canonical path to the signed resource.
- // It must include the service name (blob, table, queue or file) for version 2015-02-21 or
- // later, the storage account name, and the resource name, and must be URL-decoded.
- // -- https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
-
- // We need to replace + with %2b first to avoid being treated as a space (which is correct for query strings, but not the path component).
- canonicalizedResource = strings.Replace(canonicalizedResource, "+", "%2b", -1)
- canonicalizedResource, err = url.QueryUnescape(canonicalizedResource)
- if err != nil {
- return "", err
- }
-
- signedExpiry := expiry.UTC().Format(time.RFC3339)
-
- //If blob name is missing, resource is a container
- signedResource := "c"
- if len(name) > 0 {
- signedResource = "b"
- }
-
- protocols := "https,http"
- if HTTPSOnly {
- protocols = "https"
- }
- stringToSign, err := blobSASStringToSign(b.client.apiVersion, canonicalizedResource, signedExpiry, signedPermissions, signedIPRange, protocols)
- if err != nil {
- return "", err
- }
-
- sig := b.client.computeHmac256(stringToSign)
- sasParams := url.Values{
- "sv": {b.client.apiVersion},
- "se": {signedExpiry},
- "sr": {signedResource},
- "sp": {signedPermissions},
- "sig": {sig},
- }
-
- if b.client.apiVersion >= "2015-04-05" {
- sasParams.Add("spr", protocols)
- if signedIPRange != "" {
- sasParams.Add("sip", signedIPRange)
- }
- }
-
- sasURL, err := url.Parse(blobURL)
- if err != nil {
- return "", err
- }
- sasURL.RawQuery = sasParams.Encode()
- return sasURL.String(), nil
-}
-
-// GetBlobSASURI creates an URL to the specified blob which contains the Shared
-// Access Signature with specified permissions and expiration time.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/ee395415.aspx
-func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Time, permissions string) (string, error) {
- url, err := b.GetBlobSASURIWithSignedIPAndProtocol(container, name, expiry, permissions, "", false)
- return url, err
-}
-
-func blobSASStringToSign(signedVersion, canonicalizedResource, signedExpiry, signedPermissions string, signedIP string, protocols string) (string, error) {
- var signedStart, signedIdentifier, rscc, rscd, rsce, rscl, rsct string
-
- if signedVersion >= "2015-02-21" {
- canonicalizedResource = "/blob" + canonicalizedResource
- }
-
- // https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx#Anchor_12
- if signedVersion >= "2015-04-05" {
- return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedIP, protocols, signedVersion, rscc, rscd, rsce, rscl, rsct), nil
- }
-
- // reference: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
- if signedVersion >= "2013-08-15" {
- return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedVersion, rscc, rscd, rsce, rscl, rsct), nil
- }
-
- return "", errors.New("storage: not implemented SAS for versions earlier than 2013-08-15")
-}
diff --git a/vendor/github.com/Azure/azure-storage-go/glide.lock b/vendor/github.com/Azure/azure-storage-go/glide.lock
deleted file mode 100644
index 5d3ce8361..000000000
--- a/vendor/github.com/Azure/azure-storage-go/glide.lock
+++ /dev/null
@@ -1,14 +0,0 @@
-hash: a97c0c90fe4d23bbd8e5745431f633e75530bb611131b786d76b8e1763bce85e
-updated: 2017-02-23T09:58:57.3701584-08:00
-imports:
-- name: github.com/Azure/go-autorest
- version: ec5f4903f77ed9927ac95b19ab8e44ada64c1356
- subpackages:
- - autorest/azure
- - autorest
- - autorest/date
-- name: github.com/dgrijalva/jwt-go
- version: 2268707a8f0843315e2004ee4f1d021dc08baedf
-testImports:
-- name: gopkg.in/check.v1
- version: 20d25e2804050c1cd24a7eea1e7a6447dd0e74ec
diff --git a/vendor/github.com/Azure/azure-storage-go/glide.yaml b/vendor/github.com/Azure/azure-storage-go/glide.yaml
deleted file mode 100644
index e6783b774..000000000
--- a/vendor/github.com/Azure/azure-storage-go/glide.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-package: github.com/Azure/azure-sdk-for-go-storage
-import: []
-testImport:
-- package: gopkg.in/check.v1
diff --git a/vendor/github.com/Azure/azure-storage-go/queue.go b/vendor/github.com/Azure/azure-storage-go/queue.go
deleted file mode 100644
index 4031410ae..000000000
--- a/vendor/github.com/Azure/azure-storage-go/queue.go
+++ /dev/null
@@ -1,339 +0,0 @@
-package storage
-
-import (
- "encoding/xml"
- "fmt"
- "net/http"
- "net/url"
- "strconv"
- "strings"
-)
-
-const (
- // casing is per Golang's http.Header canonicalizing the header names.
- approximateMessagesCountHeader = "X-Ms-Approximate-Messages-Count"
- userDefinedMetadataHeaderPrefix = "X-Ms-Meta-"
-)
-
-func pathForQueue(queue string) string { return fmt.Sprintf("/%s", queue) }
-func pathForQueueMessages(queue string) string { return fmt.Sprintf("/%s/messages", queue) }
-func pathForMessage(queue, name string) string { return fmt.Sprintf("/%s/messages/%s", queue, name) }
-
-type putMessageRequest struct {
- XMLName xml.Name `xml:"QueueMessage"`
- MessageText string `xml:"MessageText"`
-}
-
-// PutMessageParameters is the set of options can be specified for Put Messsage
-// operation. A zero struct does not use any preferences for the request.
-type PutMessageParameters struct {
- VisibilityTimeout int
- MessageTTL int
-}
-
-func (p PutMessageParameters) getParameters() url.Values {
- out := url.Values{}
- if p.VisibilityTimeout != 0 {
- out.Set("visibilitytimeout", strconv.Itoa(p.VisibilityTimeout))
- }
- if p.MessageTTL != 0 {
- out.Set("messagettl", strconv.Itoa(p.MessageTTL))
- }
- return out
-}
-
-// GetMessagesParameters is the set of options can be specified for Get
-// Messsages operation. A zero struct does not use any preferences for the
-// request.
-type GetMessagesParameters struct {
- NumOfMessages int
- VisibilityTimeout int
-}
-
-func (p GetMessagesParameters) getParameters() url.Values {
- out := url.Values{}
- if p.NumOfMessages != 0 {
- out.Set("numofmessages", strconv.Itoa(p.NumOfMessages))
- }
- if p.VisibilityTimeout != 0 {
- out.Set("visibilitytimeout", strconv.Itoa(p.VisibilityTimeout))
- }
- return out
-}
-
-// PeekMessagesParameters is the set of options can be specified for Peek
-// Messsage operation. A zero struct does not use any preferences for the
-// request.
-type PeekMessagesParameters struct {
- NumOfMessages int
-}
-
-func (p PeekMessagesParameters) getParameters() url.Values {
- out := url.Values{"peekonly": {"true"}} // Required for peek operation
- if p.NumOfMessages != 0 {
- out.Set("numofmessages", strconv.Itoa(p.NumOfMessages))
- }
- return out
-}
-
-// UpdateMessageParameters is the set of options can be specified for Update Messsage
-// operation. A zero struct does not use any preferences for the request.
-type UpdateMessageParameters struct {
- PopReceipt string
- VisibilityTimeout int
-}
-
-func (p UpdateMessageParameters) getParameters() url.Values {
- out := url.Values{}
- if p.PopReceipt != "" {
- out.Set("popreceipt", p.PopReceipt)
- }
- if p.VisibilityTimeout != 0 {
- out.Set("visibilitytimeout", strconv.Itoa(p.VisibilityTimeout))
- }
- return out
-}
-
-// GetMessagesResponse represents a response returned from Get Messages
-// operation.
-type GetMessagesResponse struct {
- XMLName xml.Name `xml:"QueueMessagesList"`
- QueueMessagesList []GetMessageResponse `xml:"QueueMessage"`
-}
-
-// GetMessageResponse represents a QueueMessage object returned from Get
-// Messages operation response.
-type GetMessageResponse struct {
- MessageID string `xml:"MessageId"`
- InsertionTime string `xml:"InsertionTime"`
- ExpirationTime string `xml:"ExpirationTime"`
- PopReceipt string `xml:"PopReceipt"`
- TimeNextVisible string `xml:"TimeNextVisible"`
- DequeueCount int `xml:"DequeueCount"`
- MessageText string `xml:"MessageText"`
-}
-
-// PeekMessagesResponse represents a response returned from Get Messages
-// operation.
-type PeekMessagesResponse struct {
- XMLName xml.Name `xml:"QueueMessagesList"`
- QueueMessagesList []PeekMessageResponse `xml:"QueueMessage"`
-}
-
-// PeekMessageResponse represents a QueueMessage object returned from Peek
-// Messages operation response.
-type PeekMessageResponse struct {
- MessageID string `xml:"MessageId"`
- InsertionTime string `xml:"InsertionTime"`
- ExpirationTime string `xml:"ExpirationTime"`
- DequeueCount int `xml:"DequeueCount"`
- MessageText string `xml:"MessageText"`
-}
-
-// QueueMetadataResponse represents user defined metadata and queue
-// properties on a specific queue.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179384.aspx
-type QueueMetadataResponse struct {
- ApproximateMessageCount int
- UserDefinedMetadata map[string]string
-}
-
-// SetMetadata operation sets user-defined metadata on the specified queue.
-// Metadata is associated with the queue as name-value pairs.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179348.aspx
-func (c QueueServiceClient) SetMetadata(name string, metadata map[string]string) error {
- uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{"comp": []string{"metadata"}})
- metadata = c.client.protectUserAgent(metadata)
- headers := c.client.getStandardHeaders()
- for k, v := range metadata {
- headers[userDefinedMetadataHeaderPrefix+k] = v
- }
-
- resp, err := c.client.exec(http.MethodPut, uri, headers, nil, c.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
-
- return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
-}
-
-// GetMetadata operation retrieves user-defined metadata and queue
-// properties on the specified queue. Metadata is associated with
-// the queue as name-values pairs.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179384.aspx
-//
-// Because the way Golang's http client (and http.Header in particular)
-// canonicalize header names, the returned metadata names would always
-// be all lower case.
-func (c QueueServiceClient) GetMetadata(name string) (QueueMetadataResponse, error) {
- qm := QueueMetadataResponse{}
- qm.UserDefinedMetadata = make(map[string]string)
- uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{"comp": []string{"metadata"}})
- headers := c.client.getStandardHeaders()
- resp, err := c.client.exec(http.MethodGet, uri, headers, nil, c.auth)
- if err != nil {
- return qm, err
- }
- defer readAndCloseBody(resp.body)
-
- for k, v := range resp.headers {
- if len(v) != 1 {
- return qm, fmt.Errorf("Unexpected number of values (%d) in response header '%s'", len(v), k)
- }
-
- value := v[0]
-
- if k == approximateMessagesCountHeader {
- qm.ApproximateMessageCount, err = strconv.Atoi(value)
- if err != nil {
- return qm, fmt.Errorf("Unexpected value in response header '%s': '%s' ", k, value)
- }
- } else if strings.HasPrefix(k, userDefinedMetadataHeaderPrefix) {
- name := strings.TrimPrefix(k, userDefinedMetadataHeaderPrefix)
- qm.UserDefinedMetadata[strings.ToLower(name)] = value
- }
- }
-
- return qm, checkRespCode(resp.statusCode, []int{http.StatusOK})
-}
-
-// CreateQueue operation creates a queue under the given account.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179342.aspx
-func (c QueueServiceClient) CreateQueue(name string) error {
- uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{})
- headers := c.client.getStandardHeaders()
- resp, err := c.client.exec(http.MethodPut, uri, headers, nil, c.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
- return checkRespCode(resp.statusCode, []int{http.StatusCreated})
-}
-
-// DeleteQueue operation permanently deletes the specified queue.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179436.aspx
-func (c QueueServiceClient) DeleteQueue(name string) error {
- uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{})
- resp, err := c.client.exec(http.MethodDelete, uri, c.client.getStandardHeaders(), nil, c.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
- return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
-}
-
-// QueueExists returns true if a queue with given name exists.
-func (c QueueServiceClient) QueueExists(name string) (bool, error) {
- uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{"comp": {"metadata"}})
- resp, err := c.client.exec(http.MethodGet, uri, c.client.getStandardHeaders(), nil, c.auth)
- if resp != nil && (resp.statusCode == http.StatusOK || resp.statusCode == http.StatusNotFound) {
- return resp.statusCode == http.StatusOK, nil
- }
-
- return false, err
-}
-
-// PutMessage operation adds a new message to the back of the message queue.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179346.aspx
-func (c QueueServiceClient) PutMessage(queue string, message string, params PutMessageParameters) error {
- uri := c.client.getEndpoint(queueServiceName, pathForQueueMessages(queue), params.getParameters())
- req := putMessageRequest{MessageText: message}
- body, nn, err := xmlMarshal(req)
- if err != nil {
- return err
- }
- headers := c.client.getStandardHeaders()
- headers["Content-Length"] = strconv.Itoa(nn)
- resp, err := c.client.exec(http.MethodPost, uri, headers, body, c.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
- return checkRespCode(resp.statusCode, []int{http.StatusCreated})
-}
-
-// ClearMessages operation deletes all messages from the specified queue.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179454.aspx
-func (c QueueServiceClient) ClearMessages(queue string) error {
- uri := c.client.getEndpoint(queueServiceName, pathForQueueMessages(queue), url.Values{})
- resp, err := c.client.exec(http.MethodDelete, uri, c.client.getStandardHeaders(), nil, c.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
- return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
-}
-
-// GetMessages operation retrieves one or more messages from the front of the
-// queue.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179474.aspx
-func (c QueueServiceClient) GetMessages(queue string, params GetMessagesParameters) (GetMessagesResponse, error) {
- var r GetMessagesResponse
- uri := c.client.getEndpoint(queueServiceName, pathForQueueMessages(queue), params.getParameters())
- resp, err := c.client.exec(http.MethodGet, uri, c.client.getStandardHeaders(), nil, c.auth)
- if err != nil {
- return r, err
- }
- defer resp.body.Close()
- err = xmlUnmarshal(resp.body, &r)
- return r, err
-}
-
-// PeekMessages retrieves one or more messages from the front of the queue, but
-// does not alter the visibility of the message.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179472.aspx
-func (c QueueServiceClient) PeekMessages(queue string, params PeekMessagesParameters) (PeekMessagesResponse, error) {
- var r PeekMessagesResponse
- uri := c.client.getEndpoint(queueServiceName, pathForQueueMessages(queue), params.getParameters())
- resp, err := c.client.exec(http.MethodGet, uri, c.client.getStandardHeaders(), nil, c.auth)
- if err != nil {
- return r, err
- }
- defer resp.body.Close()
- err = xmlUnmarshal(resp.body, &r)
- return r, err
-}
-
-// DeleteMessage operation deletes the specified message.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/dd179347.aspx
-func (c QueueServiceClient) DeleteMessage(queue, messageID, popReceipt string) error {
- uri := c.client.getEndpoint(queueServiceName, pathForMessage(queue, messageID), url.Values{
- "popreceipt": {popReceipt}})
- resp, err := c.client.exec(http.MethodDelete, uri, c.client.getStandardHeaders(), nil, c.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
- return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
-}
-
-// UpdateMessage operation deletes the specified message.
-//
-// See https://msdn.microsoft.com/en-us/library/azure/hh452234.aspx
-func (c QueueServiceClient) UpdateMessage(queue string, messageID string, message string, params UpdateMessageParameters) error {
- uri := c.client.getEndpoint(queueServiceName, pathForMessage(queue, messageID), params.getParameters())
- req := putMessageRequest{MessageText: message}
- body, nn, err := xmlMarshal(req)
- if err != nil {
- return err
- }
- headers := c.client.getStandardHeaders()
- headers["Content-Length"] = fmt.Sprintf("%d", nn)
- resp, err := c.client.exec(http.MethodPut, uri, headers, body, c.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
- return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
-}
diff --git a/vendor/github.com/Azure/azure-storage-go/table.go b/vendor/github.com/Azure/azure-storage-go/table.go
deleted file mode 100644
index 4123746e5..000000000
--- a/vendor/github.com/Azure/azure-storage-go/table.go
+++ /dev/null
@@ -1,254 +0,0 @@
-package storage
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "net/url"
- "strconv"
- "time"
-)
-
-// AzureTable is the typedef of the Azure Table name
-type AzureTable string
-
-const (
- tablesURIPath = "/Tables"
-)
-
-type createTableRequest struct {
- TableName string `json:"TableName"`
-}
-
-// TableAccessPolicy are used for SETTING table policies
-type TableAccessPolicy struct {
- ID string
- StartTime time.Time
- ExpiryTime time.Time
- CanRead bool
- CanAppend bool
- CanUpdate bool
- CanDelete bool
-}
-
-func pathForTable(table AzureTable) string { return fmt.Sprintf("%s", table) }
-
-func (c *TableServiceClient) getStandardHeaders() map[string]string {
- return map[string]string{
- "x-ms-version": "2015-02-21",
- "x-ms-date": currentTimeRfc1123Formatted(),
- "Accept": "application/json;odata=nometadata",
- "Accept-Charset": "UTF-8",
- "Content-Type": "application/json",
- userAgentHeader: c.client.userAgent,
- }
-}
-
-// QueryTables returns the tables created in the
-// *TableServiceClient storage account.
-func (c *TableServiceClient) QueryTables() ([]AzureTable, error) {
- uri := c.client.getEndpoint(tableServiceName, tablesURIPath, url.Values{})
-
- headers := c.getStandardHeaders()
- headers["Content-Length"] = "0"
-
- resp, err := c.client.execInternalJSON(http.MethodGet, uri, headers, nil, c.auth)
- if err != nil {
- return nil, err
- }
- defer resp.body.Close()
-
- if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
- ioutil.ReadAll(resp.body)
- return nil, err
- }
-
- buf := new(bytes.Buffer)
- if _, err := buf.ReadFrom(resp.body); err != nil {
- return nil, err
- }
-
- var respArray queryTablesResponse
- if err := json.Unmarshal(buf.Bytes(), &respArray); err != nil {
- return nil, err
- }
-
- s := make([]AzureTable, len(respArray.TableName))
- for i, elem := range respArray.TableName {
- s[i] = AzureTable(elem.TableName)
- }
-
- return s, nil
-}
-
-// CreateTable creates the table given the specific
-// name. This function fails if the name is not compliant
-// with the specification or the tables already exists.
-func (c *TableServiceClient) CreateTable(table AzureTable) error {
- uri := c.client.getEndpoint(tableServiceName, tablesURIPath, url.Values{})
-
- headers := c.getStandardHeaders()
-
- req := createTableRequest{TableName: string(table)}
- buf := new(bytes.Buffer)
-
- if err := json.NewEncoder(buf).Encode(req); err != nil {
- return err
- }
-
- headers["Content-Length"] = fmt.Sprintf("%d", buf.Len())
-
- resp, err := c.client.execInternalJSON(http.MethodPost, uri, headers, buf, c.auth)
-
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
-
- if err := checkRespCode(resp.statusCode, []int{http.StatusCreated}); err != nil {
- return err
- }
-
- return nil
-}
-
-// DeleteTable deletes the table given the specific
-// name. This function fails if the table is not present.
-// Be advised: DeleteTable deletes all the entries
-// that may be present.
-func (c *TableServiceClient) DeleteTable(table AzureTable) error {
- uri := c.client.getEndpoint(tableServiceName, tablesURIPath, url.Values{})
- uri += fmt.Sprintf("('%s')", string(table))
-
- headers := c.getStandardHeaders()
-
- headers["Content-Length"] = "0"
-
- resp, err := c.client.execInternalJSON(http.MethodDelete, uri, headers, nil, c.auth)
-
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
-
- if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil {
- return err
-
- }
- return nil
-}
-
-// SetTablePermissions sets up table ACL permissions as per REST details https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Set-Table-ACL
-func (c *TableServiceClient) SetTablePermissions(table AzureTable, policies []TableAccessPolicy, timeout uint) (err error) {
- params := url.Values{"comp": {"acl"}}
-
- if timeout > 0 {
- params.Add("timeout", fmt.Sprint(timeout))
- }
-
- uri := c.client.getEndpoint(tableServiceName, string(table), params)
- headers := c.client.getStandardHeaders()
-
- body, length, err := generateTableACLPayload(policies)
- if err != nil {
- return err
- }
- headers["Content-Length"] = fmt.Sprintf("%v", length)
-
- resp, err := c.client.execInternalJSON(http.MethodPut, uri, headers, body, c.auth)
- if err != nil {
- return err
- }
- defer readAndCloseBody(resp.body)
-
- if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil {
- return err
- }
- return nil
-}
-
-func generateTableACLPayload(policies []TableAccessPolicy) (io.Reader, int, error) {
- sil := SignedIdentifiers{
- SignedIdentifiers: []SignedIdentifier{},
- }
- for _, tap := range policies {
- permission := generateTablePermissions(&tap)
- signedIdentifier := convertAccessPolicyToXMLStructs(tap.ID, tap.StartTime, tap.ExpiryTime, permission)
- sil.SignedIdentifiers = append(sil.SignedIdentifiers, signedIdentifier)
- }
- return xmlMarshal(sil)
-}
-
-// GetTablePermissions gets the table ACL permissions, as per REST details https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-table-acl
-func (c *TableServiceClient) GetTablePermissions(table AzureTable, timeout int) (permissionResponse []TableAccessPolicy, err error) {
- params := url.Values{"comp": {"acl"}}
-
- if timeout > 0 {
- params.Add("timeout", strconv.Itoa(timeout))
- }
-
- uri := c.client.getEndpoint(tableServiceName, string(table), params)
- headers := c.client.getStandardHeaders()
- resp, err := c.client.execInternalJSON(http.MethodGet, uri, headers, nil, c.auth)
- if err != nil {
- return nil, err
- }
- defer resp.body.Close()
-
- if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
- ioutil.ReadAll(resp.body)
- return nil, err
- }
-
- var ap AccessPolicy
- err = xmlUnmarshal(resp.body, &ap.SignedIdentifiersList)
- if err != nil {
- return nil, err
- }
- out := updateTableAccessPolicy(ap)
- return out, nil
-}
-
-func updateTableAccessPolicy(ap AccessPolicy) []TableAccessPolicy {
- out := []TableAccessPolicy{}
- for _, policy := range ap.SignedIdentifiersList.SignedIdentifiers {
- tap := TableAccessPolicy{
- ID: policy.ID,
- StartTime: policy.AccessPolicy.StartTime,
- ExpiryTime: policy.AccessPolicy.ExpiryTime,
- }
- tap.CanRead = updatePermissions(policy.AccessPolicy.Permission, "r")
- tap.CanAppend = updatePermissions(policy.AccessPolicy.Permission, "a")
- tap.CanUpdate = updatePermissions(policy.AccessPolicy.Permission, "u")
- tap.CanDelete = updatePermissions(policy.AccessPolicy.Permission, "d")
-
- out = append(out, tap)
- }
- return out
-}
-
-func generateTablePermissions(tap *TableAccessPolicy) (permissions string) {
- // generate the permissions string (raud).
- // still want the end user API to have bool flags.
- permissions = ""
-
- if tap.CanRead {
- permissions += "r"
- }
-
- if tap.CanAppend {
- permissions += "a"
- }
-
- if tap.CanUpdate {
- permissions += "u"
- }
-
- if tap.CanDelete {
- permissions += "d"
- }
- return permissions
-}
diff --git a/vendor/github.com/Azure/azure-storage-go/table_entities.go b/vendor/github.com/Azure/azure-storage-go/table_entities.go
deleted file mode 100644
index 36413a0cf..000000000
--- a/vendor/github.com/Azure/azure-storage-go/table_entities.go
+++ /dev/null
@@ -1,354 +0,0 @@
-package storage
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "reflect"
-)
-
-// Annotating as secure for gas scanning
-/* #nosec */
-const (
- partitionKeyNode = "PartitionKey"
- rowKeyNode = "RowKey"
- tag = "table"
- tagIgnore = "-"
- continuationTokenPartitionKeyHeader = "X-Ms-Continuation-Nextpartitionkey"
- continuationTokenRowHeader = "X-Ms-Continuation-Nextrowkey"
- maxTopParameter = 1000
-)
-
-type queryTablesResponse struct {
- TableName []struct {
- TableName string `json:"TableName"`
- } `json:"value"`
-}
-
-const (
- tableOperationTypeInsert = iota
- tableOperationTypeUpdate = iota
- tableOperationTypeMerge = iota
- tableOperationTypeInsertOrReplace = iota
- tableOperationTypeInsertOrMerge = iota
-)
-
-type tableOperation int
-
-// TableEntity interface specifies
-// the functions needed to support
-// marshaling and unmarshaling into
-// Azure Tables. The struct must only contain
-// simple types because Azure Tables do not
-// support hierarchy.
-type TableEntity interface {
- PartitionKey() string
- RowKey() string
- SetPartitionKey(string) error
- SetRowKey(string) error
-}
-
-// ContinuationToken is an opaque (ie not useful to inspect)
-// struct that Get... methods can return if there are more
-// entries to be returned than the ones already
-// returned. Just pass it to the same function to continue
-// receiving the remaining entries.
-type ContinuationToken struct {
- NextPartitionKey string
- NextRowKey string
-}
-
-type getTableEntriesResponse struct {
- Elements []map[string]interface{} `json:"value"`
-}
-
-// QueryTableEntities queries the specified table and returns the unmarshaled
-// entities of type retType.
-// top parameter limits the returned entries up to top. Maximum top
-// allowed by Azure API is 1000. In case there are more than top entries to be
-// returned the function will return a non nil *ContinuationToken. You can call the
-// same function again passing the received ContinuationToken as previousContToken
-// parameter in order to get the following entries. The query parameter
-// is the odata query. To retrieve all the entries pass the empty string.
-// The function returns a pointer to a TableEntity slice, the *ContinuationToken
-// if there are more entries to be returned and an error in case something went
-// wrong.
-//
-// Example:
-// entities, cToken, err = tSvc.QueryTableEntities("table", cToken, reflect.TypeOf(entity), 20, "")
-func (c *TableServiceClient) QueryTableEntities(tableName AzureTable, previousContToken *ContinuationToken, retType reflect.Type, top int, query string) ([]TableEntity, *ContinuationToken, error) {
- if top > maxTopParameter {
- return nil, nil, fmt.Errorf("top accepts at maximum %d elements. Requested %d instead", maxTopParameter, top)
- }
-
- uri := c.client.getEndpoint(tableServiceName, pathForTable(tableName), url.Values{})
- uri += fmt.Sprintf("?$top=%d", top)
- if query != "" {
- uri += fmt.Sprintf("&$filter=%s", url.QueryEscape(query))
- }
-
- if previousContToken != nil {
- uri += fmt.Sprintf("&NextPartitionKey=%s&NextRowKey=%s", previousContToken.NextPartitionKey, previousContToken.NextRowKey)
- }
-
- headers := c.getStandardHeaders()
-
- headers["Content-Length"] = "0"
-
- resp, err := c.client.execInternalJSON(http.MethodGet, uri, headers, nil, c.auth)
-
- if err != nil {
- return nil, nil, err
- }
-
- contToken := extractContinuationTokenFromHeaders(resp.headers)
-
- defer resp.body.Close()
-
- if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil {
- return nil, contToken, err
- }
-
- retEntries, err := deserializeEntity(retType, resp.body)
- if err != nil {
- return nil, contToken, err
- }
-
- return retEntries, contToken, nil
-}
-
-// InsertEntity inserts an entity in the specified table.
-// The function fails if there is an entity with the same
-// PartitionKey and RowKey in the table.
-func (c *TableServiceClient) InsertEntity(table AzureTable, entity TableEntity) error {
- sc, err := c.execTable(table, entity, false, http.MethodPost)
- if err != nil {
- return err
- }
-
- return checkRespCode(sc, []int{http.StatusCreated})
-}
-
-func (c *TableServiceClient) execTable(table AzureTable, entity TableEntity, specifyKeysInURL bool, method string) (int, error) {
- uri := c.client.getEndpoint(tableServiceName, pathForTable(table), url.Values{})
- if specifyKeysInURL {
- uri += fmt.Sprintf("(PartitionKey='%s',RowKey='%s')", url.QueryEscape(entity.PartitionKey()), url.QueryEscape(entity.RowKey()))
- }
-
- headers := c.getStandardHeaders()
-
- var buf bytes.Buffer
-
- if err := injectPartitionAndRowKeys(entity, &buf); err != nil {
- return 0, err
- }
-
- headers["Content-Length"] = fmt.Sprintf("%d", buf.Len())
-
- resp, err := c.client.execInternalJSON(method, uri, headers, &buf, c.auth)
-
- if err != nil {
- return 0, err
- }
-
- defer resp.body.Close()
-
- return resp.statusCode, nil
-}
-
-// UpdateEntity updates the contents of an entity with the
-// one passed as parameter. The function fails if there is no entity
-// with the same PartitionKey and RowKey in the table.
-func (c *TableServiceClient) UpdateEntity(table AzureTable, entity TableEntity) error {
- sc, err := c.execTable(table, entity, true, http.MethodPut)
- if err != nil {
- return err
- }
-
- return checkRespCode(sc, []int{http.StatusNoContent})
-}
-
-// MergeEntity merges the contents of an entity with the
-// one passed as parameter.
-// The function fails if there is no entity
-// with the same PartitionKey and RowKey in the table.
-func (c *TableServiceClient) MergeEntity(table AzureTable, entity TableEntity) error {
- sc, err := c.execTable(table, entity, true, "MERGE")
- if err != nil {
- return err
- }
-
- return checkRespCode(sc, []int{http.StatusNoContent})
-}
-
-// DeleteEntityWithoutCheck deletes the entity matching by
-// PartitionKey and RowKey. There is no check on IfMatch
-// parameter so the entity is always deleted.
-// The function fails if there is no entity
-// with the same PartitionKey and RowKey in the table.
-func (c *TableServiceClient) DeleteEntityWithoutCheck(table AzureTable, entity TableEntity) error {
- return c.DeleteEntity(table, entity, "*")
-}
-
-// DeleteEntity deletes the entity matching by
-// PartitionKey, RowKey and ifMatch field.
-// The function fails if there is no entity
-// with the same PartitionKey and RowKey in the table or
-// the ifMatch is different.
-func (c *TableServiceClient) DeleteEntity(table AzureTable, entity TableEntity, ifMatch string) error {
- uri := c.client.getEndpoint(tableServiceName, pathForTable(table), url.Values{})
- uri += fmt.Sprintf("(PartitionKey='%s',RowKey='%s')", url.QueryEscape(entity.PartitionKey()), url.QueryEscape(entity.RowKey()))
-
- headers := c.getStandardHeaders()
-
- headers["Content-Length"] = "0"
- headers["If-Match"] = ifMatch
-
- resp, err := c.client.execInternalJSON(http.MethodDelete, uri, headers, nil, c.auth)
-
- if err != nil {
- return err
- }
- defer resp.body.Close()
-
- if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil {
- return err
- }
-
- return nil
-}
-
-// InsertOrReplaceEntity inserts an entity in the specified table
-// or replaced the existing one.
-func (c *TableServiceClient) InsertOrReplaceEntity(table AzureTable, entity TableEntity) error {
- sc, err := c.execTable(table, entity, true, http.MethodPut)
- if err != nil {
- return err
- }
-
- return checkRespCode(sc, []int{http.StatusNoContent})
-}
-
-// InsertOrMergeEntity inserts an entity in the specified table
-// or merges the existing one.
-func (c *TableServiceClient) InsertOrMergeEntity(table AzureTable, entity TableEntity) error {
- sc, err := c.execTable(table, entity, true, "MERGE")
- if err != nil {
- return err
- }
-
- return checkRespCode(sc, []int{http.StatusNoContent})
-}
-
-func injectPartitionAndRowKeys(entity TableEntity, buf *bytes.Buffer) error {
- if err := json.NewEncoder(buf).Encode(entity); err != nil {
- return err
- }
-
- dec := make(map[string]interface{})
- if err := json.NewDecoder(buf).Decode(&dec); err != nil {
- return err
- }
-
- // Inject PartitionKey and RowKey
- dec[partitionKeyNode] = entity.PartitionKey()
- dec[rowKeyNode] = entity.RowKey()
-
- // Remove tagged fields
- // The tag is defined in the const section
- // This is useful to avoid storing the PartitionKey and RowKey twice.
- numFields := reflect.ValueOf(entity).Elem().NumField()
- for i := 0; i < numFields; i++ {
- f := reflect.ValueOf(entity).Elem().Type().Field(i)
-
- if f.Tag.Get(tag) == tagIgnore {
- // we must look for its JSON name in the dictionary
- // as the user can rename it using a tag
- jsonName := f.Name
- if f.Tag.Get("json") != "" {
- jsonName = f.Tag.Get("json")
- }
- delete(dec, jsonName)
- }
- }
-
- buf.Reset()
-
- if err := json.NewEncoder(buf).Encode(&dec); err != nil {
- return err
- }
-
- return nil
-}
-
-func deserializeEntity(retType reflect.Type, reader io.Reader) ([]TableEntity, error) {
- buf := new(bytes.Buffer)
-
- var ret getTableEntriesResponse
- if err := json.NewDecoder(reader).Decode(&ret); err != nil {
- return nil, err
- }
-
- tEntries := make([]TableEntity, len(ret.Elements))
-
- for i, entry := range ret.Elements {
-
- buf.Reset()
- if err := json.NewEncoder(buf).Encode(entry); err != nil {
- return nil, err
- }
-
- dec := make(map[string]interface{})
- if err := json.NewDecoder(buf).Decode(&dec); err != nil {
- return nil, err
- }
-
- var pKey, rKey string
- // strip pk and rk
- for key, val := range dec {
- switch key {
- case partitionKeyNode:
- pKey = val.(string)
- case rowKeyNode:
- rKey = val.(string)
- }
- }
-
- delete(dec, partitionKeyNode)
- delete(dec, rowKeyNode)
-
- buf.Reset()
- if err := json.NewEncoder(buf).Encode(dec); err != nil {
- return nil, err
- }
-
- // Create a empty retType instance
- tEntries[i] = reflect.New(retType.Elem()).Interface().(TableEntity)
- // Popolate it with the values
- if err := json.NewDecoder(buf).Decode(&tEntries[i]); err != nil {
- return nil, err
- }
-
- // Reset PartitionKey and RowKey
- if err := tEntries[i].SetPartitionKey(pKey); err != nil {
- return nil, err
- }
- if err := tEntries[i].SetRowKey(rKey); err != nil {
- return nil, err
- }
- }
-
- return tEntries, nil
-}
-
-func extractContinuationTokenFromHeaders(h http.Header) *ContinuationToken {
- ct := ContinuationToken{h.Get(continuationTokenPartitionKeyHeader), h.Get(continuationTokenRowHeader)}
-
- if ct.NextPartitionKey != "" && ct.NextRowKey != "" {
- return &ct
- }
- return nil
-}
diff --git a/vendor/github.com/Azure/azure-storage-go/tableserviceclient.go b/vendor/github.com/Azure/azure-storage-go/tableserviceclient.go
deleted file mode 100644
index ee5e0a867..000000000
--- a/vendor/github.com/Azure/azure-storage-go/tableserviceclient.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package storage
-
-// TableServiceClient contains operations for Microsoft Azure Table Storage
-// Service.
-type TableServiceClient struct {
- client Client
- auth authentication
-}
-
-// GetServiceProperties gets the properties of your storage account's table service.
-// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-table-service-properties
-func (c *TableServiceClient) GetServiceProperties() (*ServiceProperties, error) {
- return c.client.getServiceProperties(tableServiceName, c.auth)
-}
-
-// SetServiceProperties sets the properties of your storage account's table service.
-// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-table-service-properties
-func (c *TableServiceClient) SetServiceProperties(props ServiceProperties) error {
- return c.client.setServiceProperties(props, tableServiceName, c.auth)
-}
diff --git a/vendor/github.com/Azure/azure-storage-go/util.go b/vendor/github.com/Azure/azure-storage-go/util.go
deleted file mode 100644
index 57ca1b6d9..000000000
--- a/vendor/github.com/Azure/azure-storage-go/util.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package storage
-
-import (
- "bytes"
- "crypto/hmac"
- "crypto/sha256"
- "encoding/base64"
- "encoding/xml"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "net/url"
- "reflect"
- "time"
-)
-
-func (c Client) computeHmac256(message string) string {
- h := hmac.New(sha256.New, c.accountKey)
- h.Write([]byte(message))
- return base64.StdEncoding.EncodeToString(h.Sum(nil))
-}
-
-func currentTimeRfc1123Formatted() string {
- return timeRfc1123Formatted(time.Now().UTC())
-}
-
-func timeRfc1123Formatted(t time.Time) string {
- return t.Format(http.TimeFormat)
-}
-
-func mergeParams(v1, v2 url.Values) url.Values {
- out := url.Values{}
- for k, v := range v1 {
- out[k] = v
- }
- for k, v := range v2 {
- vals, ok := out[k]
- if ok {
- vals = append(vals, v...)
- out[k] = vals
- } else {
- out[k] = v
- }
- }
- return out
-}
-
-func prepareBlockListRequest(blocks []Block) string {
- s := ``
- for _, v := range blocks {
- s += fmt.Sprintf("<%s>%s%s>", v.Status, v.ID, v.Status)
- }
- s += ``
- return s
-}
-
-func xmlUnmarshal(body io.Reader, v interface{}) error {
- data, err := ioutil.ReadAll(body)
- if err != nil {
- return err
- }
- return xml.Unmarshal(data, v)
-}
-
-func xmlMarshal(v interface{}) (io.Reader, int, error) {
- b, err := xml.Marshal(v)
- if err != nil {
- return nil, 0, err
- }
- return bytes.NewReader(b), len(b), nil
-}
-
-func headersFromStruct(v interface{}) map[string]string {
- headers := make(map[string]string)
- value := reflect.ValueOf(v)
- for i := 0; i < value.NumField(); i++ {
- key := value.Type().Field(i).Tag.Get("header")
- val := value.Field(i).String()
- if key != "" && val != "" {
- headers[key] = val
- }
- }
- return headers
-}
diff --git a/vendor/github.com/Azure/go-autorest/autorest/retriablerequest.go b/vendor/github.com/Azure/go-autorest/autorest/retriablerequest.go
new file mode 100644
index 000000000..5cab8711b
--- /dev/null
+++ b/vendor/github.com/Azure/go-autorest/autorest/retriablerequest.go
@@ -0,0 +1,77 @@
+package autorest
+
+import (
+ "bytes"
+ "io"
+ "io/ioutil"
+ "net/http"
+)
+
+// NOTE: the GetBody() method on the http.Request object is new in 1.8.
+// at present we support 1.7 and 1.8 so for now the branches specific
+// to 1.8 have been commented out.
+
+// RetriableRequest provides facilities for retrying an HTTP request.
+type RetriableRequest struct {
+ req *http.Request
+ //rc io.ReadCloser
+ br *bytes.Reader
+ reset bool
+}
+
+// NewRetriableRequest returns a wrapper around an HTTP request that support retry logic.
+func NewRetriableRequest(req *http.Request) *RetriableRequest {
+ return &RetriableRequest{req: req}
+}
+
+// Request returns the wrapped HTTP request.
+func (rr *RetriableRequest) Request() *http.Request {
+ return rr.req
+}
+
+// Prepare signals that the request is about to be sent.
+func (rr *RetriableRequest) Prepare() (err error) {
+ // preserve the request body; this is to support retry logic as
+ // the underlying transport will always close the reqeust body
+ if rr.req.Body != nil {
+ if rr.reset {
+ /*if rr.rc != nil {
+ rr.req.Body = rr.rc
+ } else */if rr.br != nil {
+ _, err = rr.br.Seek(0, io.SeekStart)
+ }
+ rr.reset = false
+ if err != nil {
+ return err
+ }
+ }
+ /*if rr.req.GetBody != nil {
+ // this will allow us to preserve the body without having to
+ // make a copy. note we need to do this on each iteration
+ rr.rc, err = rr.req.GetBody()
+ if err != nil {
+ return err
+ }
+ } else */if rr.br == nil {
+ // fall back to making a copy (only do this once)
+ b := []byte{}
+ if rr.req.ContentLength > 0 {
+ b = make([]byte, rr.req.ContentLength)
+ _, err = io.ReadFull(rr.req.Body, b)
+ if err != nil {
+ return err
+ }
+ } else {
+ b, err = ioutil.ReadAll(rr.req.Body)
+ if err != nil {
+ return err
+ }
+ }
+ rr.br = bytes.NewReader(b)
+ rr.req.Body = ioutil.NopCloser(rr.br)
+ }
+ // indicates that the request body needs to be reset
+ rr.reset = true
+ }
+ return err
+}
diff --git a/vendor/github.com/Azure/go-autorest/autorest/sender.go b/vendor/github.com/Azure/go-autorest/autorest/sender.go
index 9c0697815..9b88f329a 100644
--- a/vendor/github.com/Azure/go-autorest/autorest/sender.go
+++ b/vendor/github.com/Azure/go-autorest/autorest/sender.go
@@ -1,9 +1,7 @@
package autorest
import (
- "bytes"
"fmt"
- "io/ioutil"
"log"
"math"
"net/http"
@@ -175,8 +173,13 @@ func DoPollForStatusCodes(duration time.Duration, delay time.Duration, codes ...
func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator {
return func(s Sender) Sender {
return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
+ rr := NewRetriableRequest(r)
for attempt := 0; attempt < attempts; attempt++ {
- resp, err = s.Do(r)
+ err = rr.Prepare()
+ if err != nil {
+ return resp, err
+ }
+ resp, err = s.Do(rr.Request())
if err == nil {
return resp, err
}
@@ -194,19 +197,15 @@ func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator {
func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) SendDecorator {
return func(s Sender) Sender {
return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
- b := []byte{}
- if r.Body != nil {
- b, err = ioutil.ReadAll(r.Body)
- if err != nil {
- return resp, err
- }
- }
-
+ rr := NewRetriableRequest(r)
// Increment to add the first call (attempts denotes number of retries)
attempts++
for attempt := 0; attempt < attempts; attempt++ {
- r.Body = ioutil.NopCloser(bytes.NewBuffer(b))
- resp, err = s.Do(r)
+ err = rr.Prepare()
+ if err != nil {
+ return resp, err
+ }
+ resp, err = s.Do(rr.Request())
if err != nil || !ResponseHasStatusCode(resp, codes...) {
return resp, err
}
@@ -224,9 +223,14 @@ func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) Se
func DoRetryForDuration(d time.Duration, backoff time.Duration) SendDecorator {
return func(s Sender) Sender {
return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
+ rr := NewRetriableRequest(r)
end := time.Now().Add(d)
for attempt := 0; time.Now().Before(end); attempt++ {
- resp, err = s.Do(r)
+ err = rr.Prepare()
+ if err != nil {
+ return resp, err
+ }
+ resp, err = s.Do(rr.Request())
if err == nil {
return resp, err
}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index c6a618971..159193820 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -55,10 +55,10 @@
"revisionTime": "2017-06-16T14:00:43Z"
},
{
- "checksumSHA1": "upfBL0B4wGsgM+xg1QhZvFgO7PU=",
- "path": "github.com/Azure/azure-storage-go",
- "revision": "32cfbe17a139c17f84be16bdf8f9c45c840a046b",
- "revisionTime": "2017-05-08T19:00:56Z"
+ "checksumSHA1": "ZQkLlysjzw2rgUe/U0Zz7oMvjw8=",
+ "path": "github.com/Azure/azure-sdk-for-go/storage",
+ "revision": "720c2a183bcccbe8a90f3159b63e1e2e5d4c9911",
+ "revisionTime": "2017-06-14T20:08:54Z"
},
{
"checksumSHA1": "23FJUX+AInYeEM2hoUMvYZtXZd4=",