...

Source file src/github.com/Azure/azure-sdk-for-go/storage/copyblob.go

Documentation: github.com/Azure/azure-sdk-for-go/storage

     1  package storage
     2  
     3  // Copyright (c) Microsoft Corporation. All rights reserved.
     4  // Licensed under the MIT License. See License.txt in the project root for license information.
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"net/http"
    10  	"net/url"
    11  	"strings"
    12  	"time"
    13  )
    14  
    15  const (
    16  	blobCopyStatusPending = "pending"
    17  	blobCopyStatusSuccess = "success"
    18  	blobCopyStatusAborted = "aborted"
    19  	blobCopyStatusFailed  = "failed"
    20  )
    21  
    22  // CopyOptions includes the options for a copy blob operation
    23  type CopyOptions struct {
    24  	Timeout   uint
    25  	Source    CopyOptionsConditions
    26  	Destiny   CopyOptionsConditions
    27  	RequestID string
    28  }
    29  
    30  // IncrementalCopyOptions includes the options for an incremental copy blob operation
    31  type IncrementalCopyOptions struct {
    32  	Timeout     uint
    33  	Destination IncrementalCopyOptionsConditions
    34  	RequestID   string
    35  }
    36  
    37  // CopyOptionsConditions includes some conditional options in a copy blob operation
    38  type CopyOptionsConditions struct {
    39  	LeaseID           string
    40  	IfModifiedSince   *time.Time
    41  	IfUnmodifiedSince *time.Time
    42  	IfMatch           string
    43  	IfNoneMatch       string
    44  }
    45  
    46  // IncrementalCopyOptionsConditions includes some conditional options in a copy blob operation
    47  type IncrementalCopyOptionsConditions struct {
    48  	IfModifiedSince   *time.Time
    49  	IfUnmodifiedSince *time.Time
    50  	IfMatch           string
    51  	IfNoneMatch       string
    52  }
    53  
    54  // Copy starts a blob copy operation and waits for the operation to
    55  // complete. sourceBlob parameter must be a canonical URL to the blob (can be
    56  // obtained using the GetURL method.) There is no SLA on blob copy and therefore
    57  // this helper method works faster on smaller files.
    58  //
    59  // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Copy-Blob
    60  func (b *Blob) Copy(sourceBlob string, options *CopyOptions) error {
    61  	copyID, err := b.StartCopy(sourceBlob, options)
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	return b.WaitForCopy(copyID)
    67  }
    68  
    69  // StartCopy starts a blob copy operation.
    70  // sourceBlob parameter must be a canonical URL to the blob (can be
    71  // obtained using the GetURL method.)
    72  //
    73  // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Copy-Blob
    74  func (b *Blob) StartCopy(sourceBlob string, options *CopyOptions) (string, error) {
    75  	params := url.Values{}
    76  	headers := b.Container.bsc.client.getStandardHeaders()
    77  	headers["x-ms-copy-source"] = sourceBlob
    78  	headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)
    79  
    80  	if options != nil {
    81  		params = addTimeout(params, options.Timeout)
    82  		headers = addToHeaders(headers, "x-ms-client-request-id", options.RequestID)
    83  		// source
    84  		headers = addToHeaders(headers, "x-ms-source-lease-id", options.Source.LeaseID)
    85  		headers = addTimeToHeaders(headers, "x-ms-source-if-modified-since", options.Source.IfModifiedSince)
    86  		headers = addTimeToHeaders(headers, "x-ms-source-if-unmodified-since", options.Source.IfUnmodifiedSince)
    87  		headers = addToHeaders(headers, "x-ms-source-if-match", options.Source.IfMatch)
    88  		headers = addToHeaders(headers, "x-ms-source-if-none-match", options.Source.IfNoneMatch)
    89  		//destiny
    90  		headers = addToHeaders(headers, "x-ms-lease-id", options.Destiny.LeaseID)
    91  		headers = addTimeToHeaders(headers, "x-ms-if-modified-since", options.Destiny.IfModifiedSince)
    92  		headers = addTimeToHeaders(headers, "x-ms-if-unmodified-since", options.Destiny.IfUnmodifiedSince)
    93  		headers = addToHeaders(headers, "x-ms-if-match", options.Destiny.IfMatch)
    94  		headers = addToHeaders(headers, "x-ms-if-none-match", options.Destiny.IfNoneMatch)
    95  	}
    96  	uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
    97  
    98  	resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
    99  	if err != nil {
   100  		return "", err
   101  	}
   102  	defer drainRespBody(resp)
   103  
   104  	if err := checkRespCode(resp, []int{http.StatusAccepted, http.StatusCreated}); err != nil {
   105  		return "", err
   106  	}
   107  
   108  	copyID := resp.Header.Get("x-ms-copy-id")
   109  	if copyID == "" {
   110  		return "", errors.New("Got empty copy id header")
   111  	}
   112  	return copyID, nil
   113  }
   114  
   115  // AbortCopyOptions includes the options for an abort blob operation
   116  type AbortCopyOptions struct {
   117  	Timeout   uint
   118  	LeaseID   string `header:"x-ms-lease-id"`
   119  	RequestID string `header:"x-ms-client-request-id"`
   120  }
   121  
   122  // AbortCopy aborts a BlobCopy which has already been triggered by the StartBlobCopy function.
   123  // copyID is generated from StartBlobCopy function.
   124  // currentLeaseID is required IF the destination blob has an active lease on it.
   125  // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Abort-Copy-Blob
   126  func (b *Blob) AbortCopy(copyID string, options *AbortCopyOptions) error {
   127  	params := url.Values{
   128  		"comp":   {"copy"},
   129  		"copyid": {copyID},
   130  	}
   131  	headers := b.Container.bsc.client.getStandardHeaders()
   132  	headers["x-ms-copy-action"] = "abort"
   133  
   134  	if options != nil {
   135  		params = addTimeout(params, options.Timeout)
   136  		headers = mergeHeaders(headers, headersFromStruct(*options))
   137  	}
   138  	uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
   139  
   140  	resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
   141  	if err != nil {
   142  		return err
   143  	}
   144  	defer drainRespBody(resp)
   145  	return checkRespCode(resp, []int{http.StatusNoContent})
   146  }
   147  
   148  // WaitForCopy loops until a BlobCopy operation is completed (or fails with error)
   149  func (b *Blob) WaitForCopy(copyID string) error {
   150  	for {
   151  		err := b.GetProperties(nil)
   152  		if err != nil {
   153  			return err
   154  		}
   155  
   156  		if b.Properties.CopyID != copyID {
   157  			return errBlobCopyIDMismatch
   158  		}
   159  
   160  		switch b.Properties.CopyStatus {
   161  		case blobCopyStatusSuccess:
   162  			return nil
   163  		case blobCopyStatusPending:
   164  			continue
   165  		case blobCopyStatusAborted:
   166  			return errBlobCopyAborted
   167  		case blobCopyStatusFailed:
   168  			return fmt.Errorf("storage: blob copy failed. Id=%s Description=%s", b.Properties.CopyID, b.Properties.CopyStatusDescription)
   169  		default:
   170  			return fmt.Errorf("storage: unhandled blob copy status: '%s'", b.Properties.CopyStatus)
   171  		}
   172  	}
   173  }
   174  
   175  // IncrementalCopyBlob copies a snapshot of a source blob and copies to referring blob
   176  // sourceBlob parameter must be a valid snapshot URL of the original blob.
   177  // THe original blob mut be public, or use a Shared Access Signature.
   178  //
   179  // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/incremental-copy-blob .
   180  func (b *Blob) IncrementalCopyBlob(sourceBlobURL string, snapshotTime time.Time, options *IncrementalCopyOptions) (string, error) {
   181  	params := url.Values{"comp": {"incrementalcopy"}}
   182  
   183  	// need formatting to 7 decimal places so it's friendly to Windows and *nix
   184  	snapshotTimeFormatted := snapshotTime.Format("2006-01-02T15:04:05.0000000Z")
   185  	u, err := url.Parse(sourceBlobURL)
   186  	if err != nil {
   187  		return "", err
   188  	}
   189  	query := u.Query()
   190  	query.Add("snapshot", snapshotTimeFormatted)
   191  	encodedQuery := query.Encode()
   192  	encodedQuery = strings.Replace(encodedQuery, "%3A", ":", -1)
   193  	u.RawQuery = encodedQuery
   194  	snapshotURL := u.String()
   195  
   196  	headers := b.Container.bsc.client.getStandardHeaders()
   197  	headers["x-ms-copy-source"] = snapshotURL
   198  
   199  	if options != nil {
   200  		addTimeout(params, options.Timeout)
   201  		headers = addToHeaders(headers, "x-ms-client-request-id", options.RequestID)
   202  		headers = addTimeToHeaders(headers, "x-ms-if-modified-since", options.Destination.IfModifiedSince)
   203  		headers = addTimeToHeaders(headers, "x-ms-if-unmodified-since", options.Destination.IfUnmodifiedSince)
   204  		headers = addToHeaders(headers, "x-ms-if-match", options.Destination.IfMatch)
   205  		headers = addToHeaders(headers, "x-ms-if-none-match", options.Destination.IfNoneMatch)
   206  	}
   207  
   208  	// get URI of destination blob
   209  	uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
   210  
   211  	resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
   212  	if err != nil {
   213  		return "", err
   214  	}
   215  	defer drainRespBody(resp)
   216  
   217  	if err := checkRespCode(resp, []int{http.StatusAccepted}); err != nil {
   218  		return "", err
   219  	}
   220  
   221  	copyID := resp.Header.Get("x-ms-copy-id")
   222  	if copyID == "" {
   223  		return "", errors.New("Got empty copy id header")
   224  	}
   225  	return copyID, nil
   226  }
   227  

View as plain text