leaseblob.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. package storage
  2. // Copyright 2017 Microsoft Corporation
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. import (
  16. "errors"
  17. "net/http"
  18. "net/url"
  19. "strconv"
  20. "time"
  21. )
  22. // lease constants.
  23. const (
  24. leaseHeaderPrefix = "x-ms-lease-"
  25. headerLeaseID = "x-ms-lease-id"
  26. leaseAction = "x-ms-lease-action"
  27. leaseBreakPeriod = "x-ms-lease-break-period"
  28. leaseDuration = "x-ms-lease-duration"
  29. leaseProposedID = "x-ms-proposed-lease-id"
  30. leaseTime = "x-ms-lease-time"
  31. acquireLease = "acquire"
  32. renewLease = "renew"
  33. changeLease = "change"
  34. releaseLease = "release"
  35. breakLease = "break"
  36. )
  37. // leasePut is common PUT code for the various acquire/release/break etc functions.
  38. func (b *Blob) leaseCommonPut(headers map[string]string, expectedStatus int, options *LeaseOptions) (http.Header, error) {
  39. params := url.Values{"comp": {"lease"}}
  40. if options != nil {
  41. params = addTimeout(params, options.Timeout)
  42. headers = mergeHeaders(headers, headersFromStruct(*options))
  43. }
  44. uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
  45. resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
  46. if err != nil {
  47. return nil, err
  48. }
  49. defer drainRespBody(resp)
  50. if err := checkRespCode(resp, []int{expectedStatus}); err != nil {
  51. return nil, err
  52. }
  53. return resp.Header, nil
  54. }
  55. // LeaseOptions includes options for all operations regarding leasing blobs
  56. type LeaseOptions struct {
  57. Timeout uint
  58. Origin string `header:"Origin"`
  59. IfMatch string `header:"If-Match"`
  60. IfNoneMatch string `header:"If-None-Match"`
  61. IfModifiedSince *time.Time `header:"If-Modified-Since"`
  62. IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
  63. RequestID string `header:"x-ms-client-request-id"`
  64. }
  65. // AcquireLease creates a lease for a blob
  66. // returns leaseID acquired
  67. // In API Versions starting on 2012-02-12, the minimum leaseTimeInSeconds is 15, the maximum
  68. // non-infinite leaseTimeInSeconds is 60. To specify an infinite lease, provide the value -1.
  69. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Lease-Blob
  70. func (b *Blob) AcquireLease(leaseTimeInSeconds int, proposedLeaseID string, options *LeaseOptions) (returnedLeaseID string, err error) {
  71. headers := b.Container.bsc.client.getStandardHeaders()
  72. headers[leaseAction] = acquireLease
  73. if leaseTimeInSeconds == -1 {
  74. // Do nothing, but don't trigger the following clauses.
  75. } else if leaseTimeInSeconds > 60 || b.Container.bsc.client.apiVersion < "2012-02-12" {
  76. leaseTimeInSeconds = 60
  77. } else if leaseTimeInSeconds < 15 {
  78. leaseTimeInSeconds = 15
  79. }
  80. headers[leaseDuration] = strconv.Itoa(leaseTimeInSeconds)
  81. if proposedLeaseID != "" {
  82. headers[leaseProposedID] = proposedLeaseID
  83. }
  84. respHeaders, err := b.leaseCommonPut(headers, http.StatusCreated, options)
  85. if err != nil {
  86. return "", err
  87. }
  88. returnedLeaseID = respHeaders.Get(http.CanonicalHeaderKey(headerLeaseID))
  89. if returnedLeaseID != "" {
  90. return returnedLeaseID, nil
  91. }
  92. return "", errors.New("LeaseID not returned")
  93. }
  94. // BreakLease breaks the lease for a blob
  95. // Returns the timeout remaining in the lease in seconds
  96. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Lease-Blob
  97. func (b *Blob) BreakLease(options *LeaseOptions) (breakTimeout int, err error) {
  98. headers := b.Container.bsc.client.getStandardHeaders()
  99. headers[leaseAction] = breakLease
  100. return b.breakLeaseCommon(headers, options)
  101. }
  102. // BreakLeaseWithBreakPeriod breaks the lease for a blob
  103. // breakPeriodInSeconds is used to determine how long until new lease can be created.
  104. // Returns the timeout remaining in the lease in seconds
  105. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Lease-Blob
  106. func (b *Blob) BreakLeaseWithBreakPeriod(breakPeriodInSeconds int, options *LeaseOptions) (breakTimeout int, err error) {
  107. headers := b.Container.bsc.client.getStandardHeaders()
  108. headers[leaseAction] = breakLease
  109. headers[leaseBreakPeriod] = strconv.Itoa(breakPeriodInSeconds)
  110. return b.breakLeaseCommon(headers, options)
  111. }
  112. // breakLeaseCommon is common code for both version of BreakLease (with and without break period)
  113. func (b *Blob) breakLeaseCommon(headers map[string]string, options *LeaseOptions) (breakTimeout int, err error) {
  114. respHeaders, err := b.leaseCommonPut(headers, http.StatusAccepted, options)
  115. if err != nil {
  116. return 0, err
  117. }
  118. breakTimeoutStr := respHeaders.Get(http.CanonicalHeaderKey(leaseTime))
  119. if breakTimeoutStr != "" {
  120. breakTimeout, err = strconv.Atoi(breakTimeoutStr)
  121. if err != nil {
  122. return 0, err
  123. }
  124. }
  125. return breakTimeout, nil
  126. }
  127. // ChangeLease changes a lease ID for a blob
  128. // Returns the new LeaseID acquired
  129. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Lease-Blob
  130. func (b *Blob) ChangeLease(currentLeaseID string, proposedLeaseID string, options *LeaseOptions) (newLeaseID string, err error) {
  131. headers := b.Container.bsc.client.getStandardHeaders()
  132. headers[leaseAction] = changeLease
  133. headers[headerLeaseID] = currentLeaseID
  134. headers[leaseProposedID] = proposedLeaseID
  135. respHeaders, err := b.leaseCommonPut(headers, http.StatusOK, options)
  136. if err != nil {
  137. return "", err
  138. }
  139. newLeaseID = respHeaders.Get(http.CanonicalHeaderKey(headerLeaseID))
  140. if newLeaseID != "" {
  141. return newLeaseID, nil
  142. }
  143. return "", errors.New("LeaseID not returned")
  144. }
  145. // ReleaseLease releases the lease for a blob
  146. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Lease-Blob
  147. func (b *Blob) ReleaseLease(currentLeaseID string, options *LeaseOptions) error {
  148. headers := b.Container.bsc.client.getStandardHeaders()
  149. headers[leaseAction] = releaseLease
  150. headers[headerLeaseID] = currentLeaseID
  151. _, err := b.leaseCommonPut(headers, http.StatusOK, options)
  152. if err != nil {
  153. return err
  154. }
  155. return nil
  156. }
  157. // RenewLease renews the lease for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
  158. func (b *Blob) RenewLease(currentLeaseID string, options *LeaseOptions) error {
  159. headers := b.Container.bsc.client.getStandardHeaders()
  160. headers[leaseAction] = renewLease
  161. headers[headerLeaseID] = currentLeaseID
  162. _, err := b.leaseCommonPut(headers, http.StatusOK, options)
  163. if err != nil {
  164. return err
  165. }
  166. return nil
  167. }