aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Azure/azure-storage-go/blob.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/Azure/azure-storage-go/blob.go')
-rw-r--r--vendor/github.com/Azure/azure-storage-go/blob.go1130
1 files changed, 0 insertions, 1130 deletions
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")
-}