azure-storage 0.14.0.preview → 0.15.0.preview

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)