azure-storage 0.14.0.preview → 0.15.0.preview

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -60,7 +60,11 @@ module Azure::Storage
60
60
  attr_writer :storage_table_host,
61
61
  :storage_blob_host,
62
62
  :storage_queue_host,
63
- :storage_file_host
63
+ :storage_file_host,
64
+ :storage_table_host_secondary,
65
+ :storage_blob_host_secondary,
66
+ :storage_queue_host_secondary,
67
+ :storage_file_host_secondary
64
68
 
65
69
  attr_reader :signer
66
70
 
@@ -76,7 +80,8 @@ module Azure::Storage
76
80
  :storage_table_host,
77
81
  :storage_blob_host,
78
82
  :storage_queue_host,
79
- :storage_file_host
83
+ :storage_file_host,
84
+ :signer
80
85
  ]
81
86
  end
82
87
  end
@@ -93,12 +98,18 @@ module Azure::Storage
93
98
  # Reset configuration options to default values
94
99
  def reset_config!(options = {})
95
100
  Azure::Storage::Configurable.keys.each do |key|
96
- value = if self == Azure::Storage
97
- Azure::Storage::Default.options[key]
98
- else
99
- Azure::Storage.send(key)
100
- end
101
+ value =
102
+ if self == Azure::Storage
103
+ Azure::Storage::Default.options[key]
104
+ else
105
+ Azure::Storage.send(key)
106
+ end
101
107
  instance_variable_set(:"@#{key}", options.fetch(key, value))
108
+
109
+ # Set the secondary endpoint if the primary one is given
110
+ if key.to_s.include? "host"
111
+ instance_variable_set(:"@#{key}_secondary", secondary_endpoint(options.fetch(key, value)))
112
+ end
102
113
  end
103
114
  self.send(:reset_agents!) if self.respond_to?(:reset_agents!)
104
115
  setup_signer_for_service
@@ -109,32 +120,48 @@ module Azure::Storage
109
120
 
110
121
  # Storage queue host
111
122
  # @return [String]
112
- def storage_queue_host
113
- @storage_queue_host || default_host(:queue)
123
+ def storage_queue_host(isSecondary = false)
124
+ if isSecondary
125
+ @storage_queue_host_secondary || default_host(:queue, true)
126
+ else
127
+ @storage_queue_host || default_host(:queue, false)
128
+ end
114
129
  end
115
130
 
116
131
  # Storage blob host
117
132
  # @return [String]
118
- def storage_blob_host
119
- @storage_blob_host || default_host(:blob)
133
+ def storage_blob_host(isSecondary = false)
134
+ if isSecondary
135
+ @storage_blob_host_secondary || default_host(:blob, true)
136
+ else
137
+ @storage_blob_host || default_host(:blob, false)
138
+ end
120
139
  end
121
140
 
122
141
  # Storage table host
123
142
  # @return [String]
124
- def storage_table_host
125
- @storage_table_host || default_host(:table)
143
+ def storage_table_host(isSecondary = false)
144
+ if isSecondary
145
+ @storage_table_host_secondary || default_host(:table, true)
146
+ else
147
+ @storage_table_host || default_host(:table, false)
148
+ end
126
149
  end
127
150
 
128
151
  # Storage file host
129
152
  # @return [String]
130
- def storage_file_host
131
- @storage_file_host || default_host(:file)
153
+ def storage_file_host(isSecondary = false)
154
+ if isSecondary
155
+ @storage_file_host_secondary || default_host(:file, true)
156
+ else
157
+ @storage_file_host || default_host(:file, false)
158
+ end
132
159
  end
133
160
 
134
161
  private
135
162
 
136
- def default_host(service)
137
- "https://#{storage_account_name}.#{service}.core.windows.net" if storage_account_name
163
+ def default_host(service, isSecondary = false)
164
+ "https://#{storage_account_name}#{isSecondary ? "-secondary" : ""}.#{service}.core.windows.net" if storage_account_name
138
165
  end
139
166
 
140
167
  def setup_options
@@ -152,6 +179,12 @@ module Azure::Storage
152
179
  fields[0]
153
180
  end
154
181
 
182
+ def secondary_endpoint(primary_endpoint)
183
+ return nil if primary_endpoint.nil?
184
+ account_name = account_name_from_endpoint primary_endpoint
185
+ primary_endpoint.sub account_name, account_name + "-secondary"
186
+ end
187
+
155
188
  def determine_account_name
156
189
  if instance_variable_get(:@storage_account_name).nil?
157
190
  hosts = [@storage_blob_host, @storage_table_host, @storage_queue_host, @storage_file_host]
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ #-------------------------------------------------------------------------
4
+ # # Copyright (c) Microsoft and contributors. All rights reserved.
5
+ #
6
+ # The MIT License(MIT)
7
+
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files(the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions :
14
+
15
+ # The above copyright notice and this permission notice shall be included in
16
+ # all copies or substantial portions of the Software.
17
+
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ # THE SOFTWARE.
25
+ #--------------------------------------------------------------------------
26
+
27
+ require "azure/core/auth/signer"
28
+ require "base64"
29
+
30
+ module Azure::Storage::Core
31
+ module Auth
32
+ class AnonymousSigner < Azure::Core::Auth::Signer
33
+ # Public: Initialize the Anonymous Signer
34
+ def initialize()
35
+ # Use mock key to initialize super class
36
+ super(Base64.strict_encode64("accesskey"))
37
+ end
38
+
39
+ def sign_request(req)
40
+ # Do nothing.
41
+ end
42
+ end
43
+ end
44
+ end
@@ -31,6 +31,7 @@ module Azure::Storage::Core::Filter
31
31
  def initialize(retry_count = nil, retry_interval = nil)
32
32
  @retry_count = retry_count
33
33
  @retry_interval = retry_interval
34
+ @request_options = {}
34
35
 
35
36
  super &:should_retry?
36
37
  end
@@ -44,6 +45,9 @@ module Azure::Storage::Core::Filter
44
45
  # response - HttpResponse. The response from the active request
45
46
  # retry_data - Hash. Stores stateful retry data
46
47
  def should_retry?(response, retry_data)
48
+ # Fill necessary information
49
+ init_retry_data retry_data
50
+
47
51
  # Applies the logic when there is subclass overrides it
48
52
  apply_retry_policy retry_data
49
53
 
@@ -60,7 +64,12 @@ module Azure::Storage::Core::Filter
60
64
  should_retry_on_local_error? retry_data
61
65
  return false unless should_retry_on_error? response, retry_data
62
66
 
63
- adjust_retry_parameter retry_data
67
+ # Determined that it needs to retry.
68
+ adjust_retry_request retry_data
69
+
70
+ wait_for_retry
71
+
72
+ retry_data[:retryable]
64
73
  end
65
74
 
66
75
  # Apply the retry policy to determine how the HTTP request should continue retrying
@@ -131,12 +140,82 @@ module Azure::Storage::Core::Filter
131
140
  return retry_data[:retryable]
132
141
  end
133
142
 
143
+ check_location(response, retry_data)
144
+
145
+ check_status_code(retry_data)
146
+
147
+ retry_data[:retryable]
148
+ end
149
+
150
+ # Adjust the retry parameter and wait for retry
151
+ def wait_for_retry
152
+ sleep @retry_interval
153
+ end
154
+
155
+ # Adjust the retry request
156
+ #
157
+ # retry_data - Hash. Stores stateful retry data
158
+ def adjust_retry_request(retry_data)
159
+ # Adjust the location first
160
+ next_location = @request_options[:target_location].nil? ? get_next_location(retry_data) : @request_options[:target_location]
161
+ retry_data[:current_location] = next_location
162
+
163
+ retry_data[:uri] =
164
+ if next_location == Azure::Storage::StorageLocation::PRIMARY
165
+ @request_options[:primary_uri]
166
+ else
167
+ @request_options[:secondary_uri]
168
+ end
169
+
170
+ # Now is the time to calculate the exact retry interval. ShouldRetry call above already
171
+ # returned back how long two requests to the same location should be apart from each other.
172
+ # However, for the reasons explained above, the time spent between the last attempt to
173
+ # the target location and current time must be subtracted from the total retry interval
174
+ # that ShouldRetry returned.
175
+ lastAttemptTime =
176
+ if retry_data[:current_location] == Azure::Storage::StorageLocation::PRIMARY
177
+ retry_data[:last_primary_attempt]
178
+ else
179
+ retry_data[:last_secondary_attempt]
180
+ end
181
+
182
+ @retry_interval =
183
+ if lastAttemptTime.nil?
184
+ 0
185
+ else
186
+ since_last_attempt = Time.now - lastAttemptTime
187
+ retry_data[:interval] - since_last_attempt
188
+ end
189
+ end
190
+
191
+ # Initialize the retry data
192
+ #
193
+ # retry_data - Hash. Stores stateful retry data
194
+ def init_retry_data(retry_data)
195
+ @request_options = retry_data[:request_options] unless retry_data[:request_options].nil?
196
+
197
+ if retry_data[:current_location].nil?
198
+ retry_data[:current_location] = Azure::Storage::Service::StorageService.get_location(@request_options[:location_mode], @request_options[:request_location_mode])
199
+ end
200
+
201
+ if retry_data[:current_location] == Azure::Storage::StorageLocation::PRIMARY
202
+ retry_data[:last_primary_attempt] = Time.now
203
+ else
204
+ retry_data[:last_secondary_attempt] = Time.now
205
+ end
206
+
207
+ end
208
+
209
+ # Check the location
210
+ #
211
+ # retry_data - Hash. Stores stateful retry data
212
+ def check_location(response, retry_data)
134
213
  # If a request sent to the secondary location fails with 404 (Not Found), it is possible
135
214
  # that the resource replication is not finished yet. So, in case of 404 only in the secondary
136
215
  # location, the failure should still be retryable.
137
- secondary_not_found = (retry_data[:current_location] === Azure::Storage::RequestLocationMode::SECONDARY_ONLY) && response.status_code === 404;
216
+ retry_data[:secondary_not_found] = (retry_data[:current_location] === Azure::Storage::StorageLocation::SECONDARY) && response.status_code === 404;
138
217
 
139
- if secondary_not_found
218
+ if retry_data[:secondary_not_found]
140
219
  retry_data[:status_code] = 500
141
220
  else
142
221
  if (response.status_code)
@@ -145,20 +224,28 @@ module Azure::Storage::Core::Filter
145
224
  retry_data[:status_code] = nil
146
225
  end
147
226
  end
227
+ end
148
228
 
229
+ # Check the status code
230
+ #
231
+ # retry_data - Hash. Stores stateful retry data
232
+ def check_status_code(retry_data)
149
233
  if (retry_data[:status_code] < 400)
150
234
  retry_data[:retryable] = false;
151
- return false;
152
235
  # Non-timeout Cases
153
236
  elsif (retry_data[:status_code] != 408)
154
237
  # Always no retry on "not implemented" and "version not supported"
155
238
  if (retry_data[:status_code] == 501 || retry_data[:status_code] == 505)
156
239
  retry_data[:retryable] = false;
157
- return false;
240
+ end
241
+
242
+ if (retry_data[:status_code] == 404)
243
+ retry_data[:retryable] = true;
244
+ return true;
158
245
  end
159
246
 
160
247
  # When absorb_conditional_errors_on_retry is set (for append blob)
161
- if (retry_data[:request_options] && retry_data[:request_options][:absorb_conditional_errors_on_retry])
248
+ if (@request_options[:absorb_conditional_errors_on_retry])
162
249
  if (retry_data[:status_code] == 412)
163
250
  # When appending block with precondition failure and their was a server error before, we ignore the error.
164
251
  if (retry_data[:last_server_error])
@@ -177,16 +264,40 @@ module Azure::Storage::Core::Filter
177
264
  retry_data[:retryable] = false;
178
265
  end
179
266
  end
180
- retry_data[:retryable]
181
267
  end
182
268
 
183
- # Adjust the retry parameter
269
+ # Get retry request destination
184
270
  #
185
271
  # retry_data - Hash. Stores stateful retry data
186
- def adjust_retry_parameter(retry_data)
187
- # TODO: Adjust the retry parameter according to the location and last attempt time
188
- sleep retry_data[:interval] if retry_data[:retryable]
189
- retry_data[:retryable]
272
+ def get_next_location(retry_data)
273
+ # In case of 404 when trying the secondary location, instead of retrying on the
274
+ # secondary, further requests should be sent only to the primary location, as it most
275
+ # probably has a higher chance of succeeding there.
276
+ if retry_data[:secondary_not_found] && @request_options[:location_mode] != Azure::Storage::LocationMode::SECONDARY_ONLY
277
+ @request_options[:location_mode] = Azure::Storage::LocationMode::PRIMARY_ONLY;
278
+ return Azure::Storage::StorageLocation::PRIMARY
279
+ end
280
+
281
+ case @request_options[:location_mode]
282
+ when Azure::Storage::LocationMode::PRIMARY_ONLY
283
+ Azure::Storage::StorageLocation::PRIMARY
284
+ when Azure::Storage::LocationMode::SECONDARY_ONLY
285
+ Azure::Storage::StorageLocation::SECONDARY
286
+ else
287
+ # request_location_mode cannot be SECONDARY_ONLY because it will be blocked at the first time
288
+ if @request_options[:request_location_mode] == Azure::Storage::RequestLocationMode::PRIMARY_ONLY
289
+ Azure::Storage::StorageLocation::PRIMARY
290
+ elsif @request_options[:request_location_mode] == Azure::Storage::RequestLocationMode::SECONDARY_ONLY
291
+ Azure::Storage::StorageLocation::SECONDARY
292
+ else
293
+ if retry_data[:current_location] === Azure::Storage::StorageLocation::PRIMARY
294
+ Azure::Storage::StorageLocation::SECONDARY
295
+ else
296
+ Azure::Storage::StorageLocation::PRIMARY
297
+ end
298
+ end
299
+ end
190
300
  end
301
+
191
302
  end
192
303
  end
@@ -140,7 +140,16 @@ module Azure::Storage
140
140
  FILE = "file"
141
141
  end
142
142
 
143
- # Specifies the location used to indicate which location the operation can be performed against.
143
+ # Specifies the location mode used to decide which location the request should be sent to.
144
+ module LocationMode
145
+ PRIMARY_ONLY = 0
146
+ PRIMARY_THEN_SECONDARY = 1
147
+ SECONDARY_ONLY = 2
148
+ SECONDARY_THEN_PRIMARY = 3
149
+ end
150
+
151
+ # Specifies the location used to indicate which location the operation (REST API) can be performed against.
152
+ # This is determined by the API and cannot be specified by the users.
144
153
  module RequestLocationMode
145
154
  PRIMARY_ONLY = 0
146
155
  SECONDARY_ONLY = 1
@@ -75,6 +75,9 @@ module Azure::Storage::File
75
75
  # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
76
76
  # in the analytics logs when storage analytics logging is enabled.
77
77
  #
78
+ # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
79
+ # which location the request should be sent to.
80
+ #
78
81
  # See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/list-directories-and-files
79
82
  #
80
83
  # Returns an Azure::Service::EnumerationResults
@@ -88,7 +91,8 @@ module Azure::Storage::File
88
91
  StorageService.with_query query, "prefix", options[:prefix].to_s if options[:prefix]
89
92
  end
90
93
 
91
- uri = directory_uri(share, directory_path, query)
94
+ options[:request_location_mode] = Azure::Storage::RequestLocationMode::PRIMARY_OR_SECONDARY
95
+ uri = directory_uri(share, directory_path, query, options)
92
96
  response = call(:get, uri, nil, {}, options)
93
97
 
94
98
  # Result
@@ -156,6 +160,8 @@ module Azure::Storage::File
156
160
  # * +:timeout+ - Integer. A timeout in seconds.
157
161
  # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
158
162
  # in the analytics logs when storage analytics logging is enabled.
163
+ # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
164
+ # which location the request should be sent to.
159
165
  #
160
166
  # See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-directory-properties
161
167
  #
@@ -166,7 +172,8 @@ module Azure::Storage::File
166
172
  query["timeout"] = options[:timeout].to_s if options[:timeout]
167
173
 
168
174
  # Call
169
- response = call(:get, directory_uri(share, directory_path, query), nil, {}, options)
175
+ options[:request_location_mode] = Azure::Storage::RequestLocationMode::PRIMARY_OR_SECONDARY
176
+ response = call(:get, directory_uri(share, directory_path, query, options), nil, {}, options)
170
177
 
171
178
  # result
172
179
  directory = Serialization.directory_from_headers(response.headers)
@@ -218,6 +225,8 @@ module Azure::Storage::File
218
225
  # * +:timeout+ - Integer. A timeout in seconds.
219
226
  # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
220
227
  # in the analytics logs when storage analytics logging is enabled.
228
+ # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
229
+ # which location the request should be sent to.
221
230
  #
222
231
  # See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-directory-metadata
223
232
  #
@@ -228,7 +237,8 @@ module Azure::Storage::File
228
237
  query["timeout"] = options[:timeout].to_s if options[:timeout]
229
238
 
230
239
  # Call
231
- response = call(:get, directory_uri(share, directory_path, query), nil, {}, options)
240
+ options[:request_location_mode] = Azure::Storage::RequestLocationMode::PRIMARY_OR_SECONDARY
241
+ response = call(:get, directory_uri(share, directory_path, query, options), nil, {}, options)
232
242
 
233
243
  # result
234
244
  directory = Serialization.directory_from_headers(response.headers)
@@ -129,6 +129,8 @@ module Azure::Storage::File
129
129
  # * +:timeout+ - Integer. A timeout in seconds.
130
130
  # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
131
131
  # in the analytics logs when storage analytics logging is enabled.
132
+ # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
133
+ # which location the request should be sent to.
132
134
  #
133
135
  # See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-file
134
136
  #
@@ -136,7 +138,9 @@ module Azure::Storage::File
136
138
  def get_file(share, directory_path, file, options = {})
137
139
  query = {}
138
140
  StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
139
- uri = file_uri(share, directory_path, file, query)
141
+
142
+ options[:request_location_mode] = Azure::Storage::RequestLocationMode::PRIMARY_OR_SECONDARY
143
+ uri = file_uri(share, directory_path, file, query, options)
140
144
 
141
145
  headers = StorageService.common_headers
142
146
  options[:start_range] = 0 if options[:end_range] && (not options[:start_range])
@@ -167,6 +171,8 @@ module Azure::Storage::File
167
171
  # * +:timeout+ - Integer. A timeout in seconds.
168
172
  # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
169
173
  # in the analytics logs when storage analytics logging is enabled.
174
+ # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
175
+ # which location the request should be sent to.
170
176
  #
171
177
  # See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-file-properties
172
178
  #
@@ -177,7 +183,8 @@ module Azure::Storage::File
177
183
 
178
184
  headers = StorageService.common_headers
179
185
 
180
- uri = file_uri(share, directory_path, file, query)
186
+ options[:request_location_mode] = Azure::Storage::RequestLocationMode::PRIMARY_OR_SECONDARY
187
+ uri = file_uri(share, directory_path, file, query, options)
181
188
 
182
189
  response = call(:head, uri, nil, headers, options)
183
190
 
@@ -361,6 +368,8 @@ module Azure::Storage::File
361
368
  # * +:timeout+ - Integer. A timeout in seconds.
362
369
  # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
363
370
  # in the analytics logs when storage analytics logging is enabled.
371
+ # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
372
+ # which location the request should be sent to.
364
373
  #
365
374
  # See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/list-ranges
366
375
  #
@@ -372,7 +381,8 @@ module Azure::Storage::File
372
381
  query = { "comp" => "rangelist" }
373
382
  StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
374
383
 
375
- uri = file_uri(share, directory_path, file, query)
384
+ options[:request_location_mode] = Azure::Storage::RequestLocationMode::PRIMARY_OR_SECONDARY
385
+ uri = file_uri(share, directory_path, file, query, options)
376
386
 
377
387
  options[:start_range] = 0 if options[:end_range] && (not options[:start_range])
378
388
 
@@ -402,6 +412,8 @@ module Azure::Storage::File
402
412
  # * +:timeout+ - Integer. A timeout in seconds.
403
413
  # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
404
414
  # in the analytics logs when storage analytics logging is enabled.
415
+ # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
416
+ # which location the request should be sent to.
405
417
  #
406
418
  # See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-file-metadata
407
419
  #
@@ -412,7 +424,8 @@ module Azure::Storage::File
412
424
  query["timeout"] = options[:timeout].to_s if options[:timeout]
413
425
 
414
426
  # Call
415
- response = call(:get, file_uri(share, directory_path, file, query), nil, {}, options)
427
+ options[:request_location_mode] = Azure::Storage::RequestLocationMode::PRIMARY_OR_SECONDARY
428
+ response = call(:get, file_uri(share, directory_path, file, query, options), nil, {}, options)
416
429
 
417
430
  # result
418
431
  result = Serialization.file_from_headers(response.headers)