202 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			202 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
package storage
 | 
						|
 | 
						|
// Copyright 2017 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.
 | 
						|
 | 
						|
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 drainRespBody(resp)
 | 
						|
 | 
						|
	if err := checkRespCode(resp, []int{expectedStatus}); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return resp.Header, 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
 | 
						|
}
 |