1 package storage
2
3
4
5
6 import (
7 "errors"
8 "net/http"
9 "net/url"
10 "strconv"
11 "time"
12 )
13
14
15 const (
16 leaseHeaderPrefix = "x-ms-lease-"
17 headerLeaseID = "x-ms-lease-id"
18 leaseAction = "x-ms-lease-action"
19 leaseBreakPeriod = "x-ms-lease-break-period"
20 leaseDuration = "x-ms-lease-duration"
21 leaseProposedID = "x-ms-proposed-lease-id"
22 leaseTime = "x-ms-lease-time"
23
24 acquireLease = "acquire"
25 renewLease = "renew"
26 changeLease = "change"
27 releaseLease = "release"
28 breakLease = "break"
29 )
30
31
32 func (b *Blob) leaseCommonPut(headers map[string]string, expectedStatus int, options *LeaseOptions) (http.Header, error) {
33 params := url.Values{"comp": {"lease"}}
34
35 if options != nil {
36 params = addTimeout(params, options.Timeout)
37 headers = mergeHeaders(headers, headersFromStruct(*options))
38 }
39 uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
40
41 resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
42 if err != nil {
43 return nil, err
44 }
45 defer drainRespBody(resp)
46
47 if err := checkRespCode(resp, []int{expectedStatus}); err != nil {
48 return nil, err
49 }
50
51 return resp.Header, nil
52 }
53
54
55 type LeaseOptions struct {
56 Timeout uint
57 Origin string `header:"Origin"`
58 IfMatch string `header:"If-Match"`
59 IfNoneMatch string `header:"If-None-Match"`
60 IfModifiedSince *time.Time `header:"If-Modified-Since"`
61 IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
62 RequestID string `header:"x-ms-client-request-id"`
63 }
64
65
66
67
68
69
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
74 if leaseTimeInSeconds == -1 {
75
76 } else if leaseTimeInSeconds > 60 || b.Container.bsc.client.apiVersion < "2012-02-12" {
77 leaseTimeInSeconds = 60
78 } else if leaseTimeInSeconds < 15 {
79 leaseTimeInSeconds = 15
80 }
81
82 headers[leaseDuration] = strconv.Itoa(leaseTimeInSeconds)
83
84 if proposedLeaseID != "" {
85 headers[leaseProposedID] = proposedLeaseID
86 }
87
88 respHeaders, err := b.leaseCommonPut(headers, http.StatusCreated, options)
89 if err != nil {
90 return "", err
91 }
92
93 returnedLeaseID = respHeaders.Get(http.CanonicalHeaderKey(headerLeaseID))
94
95 if returnedLeaseID != "" {
96 return returnedLeaseID, nil
97 }
98
99 return "", errors.New("LeaseID not returned")
100 }
101
102
103
104
105 func (b *Blob) BreakLease(options *LeaseOptions) (breakTimeout int, err error) {
106 headers := b.Container.bsc.client.getStandardHeaders()
107 headers[leaseAction] = breakLease
108 return b.breakLeaseCommon(headers, options)
109 }
110
111
112
113
114
115 func (b *Blob) BreakLeaseWithBreakPeriod(breakPeriodInSeconds int, options *LeaseOptions) (breakTimeout int, err error) {
116 headers := b.Container.bsc.client.getStandardHeaders()
117 headers[leaseAction] = breakLease
118 headers[leaseBreakPeriod] = strconv.Itoa(breakPeriodInSeconds)
119 return b.breakLeaseCommon(headers, options)
120 }
121
122
123 func (b *Blob) breakLeaseCommon(headers map[string]string, options *LeaseOptions) (breakTimeout int, err error) {
124
125 respHeaders, err := b.leaseCommonPut(headers, http.StatusAccepted, options)
126 if err != nil {
127 return 0, err
128 }
129
130 breakTimeoutStr := respHeaders.Get(http.CanonicalHeaderKey(leaseTime))
131 if breakTimeoutStr != "" {
132 breakTimeout, err = strconv.Atoi(breakTimeoutStr)
133 if err != nil {
134 return 0, err
135 }
136 }
137
138 return breakTimeout, nil
139 }
140
141
142
143
144 func (b *Blob) ChangeLease(currentLeaseID string, proposedLeaseID string, options *LeaseOptions) (newLeaseID string, err error) {
145 headers := b.Container.bsc.client.getStandardHeaders()
146 headers[leaseAction] = changeLease
147 headers[headerLeaseID] = currentLeaseID
148 headers[leaseProposedID] = proposedLeaseID
149
150 respHeaders, err := b.leaseCommonPut(headers, http.StatusOK, options)
151 if err != nil {
152 return "", err
153 }
154
155 newLeaseID = respHeaders.Get(http.CanonicalHeaderKey(headerLeaseID))
156 if newLeaseID != "" {
157 return newLeaseID, nil
158 }
159
160 return "", errors.New("LeaseID not returned")
161 }
162
163
164
165 func (b *Blob) ReleaseLease(currentLeaseID string, options *LeaseOptions) error {
166 headers := b.Container.bsc.client.getStandardHeaders()
167 headers[leaseAction] = releaseLease
168 headers[headerLeaseID] = currentLeaseID
169
170 _, err := b.leaseCommonPut(headers, http.StatusOK, options)
171 if err != nil {
172 return err
173 }
174
175 return nil
176 }
177
178
179 func (b *Blob) RenewLease(currentLeaseID string, options *LeaseOptions) error {
180 headers := b.Container.bsc.client.getStandardHeaders()
181 headers[leaseAction] = renewLease
182 headers[headerLeaseID] = currentLeaseID
183
184 _, err := b.leaseCommonPut(headers, http.StatusOK, options)
185 if err != nil {
186 return err
187 }
188
189 return nil
190 }
191
View as plain text