aws-sdk-core 3.125.5 → 3.130.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6097fa7cc1661cdca09ad52fbf3f6ac5d0e2cb23f12c042afb83348fbb682167
4
- data.tar.gz: 7674b35f80736d81b5f239bff160457ca5a377c576720f27aae4099298bb7dae
3
+ metadata.gz: d7804cbac83996a95b2cacbd808389798eb0b461229a9e8e2a966cd28d599aa6
4
+ data.tar.gz: ce3557bcbd4d6a5edaa1bc99fbd7e75f9b2e23eb6fadb8be06a69b91e2e8a4a7
5
5
  SHA512:
6
- metadata.gz: 8ec3ffb176c06f067369f3a58ce65fa3fe03fe017312d67a10bbeeae9dcaaaa95ddbbc295b0235fa7671037768ece22a0d6c5d2691fb93d11fd08235dcb80b15
7
- data.tar.gz: 0da7d408ebe2173564167decedeb434dc941b786eca73e207c82d93663fc85d9dadaedca6e1a643cfa375332ebd40866f791fa815bb4356a4536c48604991235
6
+ metadata.gz: d1d0b4ec0b478389290d84409869ed012e4a2ce6408d3dc5c46c618771cc970d9086eb4686dd64b740ebd5eeafe7cc0e8b6fa63d3f89202e8f2841d9a203bdbb
7
+ data.tar.gz: f4c41ffb85d712bcaf8701aa61a886837517df23e14a53c2b9201d86bfcc1fd41b5b8badded9f91aeb0ee77553c9e2af80b4c82d1e249486b5630afc1582939c
data/CHANGELOG.md CHANGED
@@ -1,6 +1,65 @@
1
1
  Unreleased Changes
2
2
  ------------------
3
3
 
4
+ 3.130.0 (2022-03-11)
5
+ ------------------
6
+
7
+ * Feature - Asynchronously refresh AWS credentials (#2641).
8
+
9
+ * Issue - Add x-amz-region-set to list of headers deleted for re-sign.
10
+
11
+ 3.129.1 (2022-03-10)
12
+ ------------------
13
+
14
+ * Issue - Make stubs thread safe by creating new responses for each operation call (#2675).
15
+
16
+ 3.129.0 (2022-03-08)
17
+ ------------------
18
+
19
+ * Feature - Add support for cases when `InstanceProfileCredentials` (IMDS) is unable to refresh credentials.
20
+
21
+ 3.128.1 (2022-03-07)
22
+ ------------------
23
+
24
+ * Issue - Fixed `Aws::PageableResponse` invalidating Ruby's global constant cache.
25
+
26
+ 3.128.0 (2022-03-04)
27
+ ------------------
28
+
29
+ * Feature - Updated Aws::STS::Client with the latest API changes.
30
+
31
+ 3.127.0 (2022-02-24)
32
+ ------------------
33
+
34
+ * Feature - Updated Aws::STS::Client with the latest API changes.
35
+
36
+ * Feature - Updated Aws::SSO::Client with the latest API changes.
37
+
38
+ * Feature - Support `HttpChecksum` trait for requests and responses.
39
+
40
+ 3.126.2 (2022-02-16)
41
+ ------------------
42
+
43
+ * Issue - Add a before_refresh callback to AssumeRoleCredentials (#2529).
44
+ * Issue - Raise a `NoSuchProfileError` when config and credentials files don't exist.
45
+
46
+ 3.126.1 (2022-02-14)
47
+ ------------------
48
+
49
+ * Issue - Set `create_time` on IMDS tokens before fetch to reduce chance of using expired tokens and retry failures due to using expired tokens.
50
+
51
+ 3.126.0 (2022-02-03)
52
+ ------------------
53
+
54
+ * Feature - Updated Aws::SSO::Client with the latest API changes.
55
+
56
+ * Feature - Add support for recursion detection.
57
+
58
+ 3.125.6 (2022-02-02)
59
+ ------------------
60
+
61
+ * Issue - Ensure default message for ServiceError is a string (#2643).
62
+
4
63
  3.125.5 (2022-01-19)
5
64
  ------------------
6
65
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.125.5
1
+ 3.130.0
@@ -17,6 +17,11 @@ module Aws
17
17
  #
18
18
  # If you omit `:client` option, a new {STS::Client} object will be
19
19
  # constructed.
20
+ #
21
+ # The AssumeRoleCredentials also provides a `before_refresh` callback
22
+ # that can be used to help manage refreshing tokens.
23
+ # `before_refresh` is called when AWS credentials are required and need
24
+ # to be refreshed and it is called with the AssumeRoleCredentials object.
20
25
  class AssumeRoleCredentials
21
26
 
22
27
  include CredentialProvider
@@ -28,6 +33,16 @@ module Aws
28
33
  # @option options [Integer] :duration_seconds
29
34
  # @option options [String] :external_id
30
35
  # @option options [STS::Client] :client
36
+ # @option options [Callable] before_refresh Proc called before
37
+ # credentials are refreshed. Useful for updating tokens.
38
+ # `before_refresh` is called when AWS credentials are
39
+ # required and need to be refreshed. Tokens can be refreshed using
40
+ # the following example:
41
+ #
42
+ # before_refresh = Proc.new do |assume_role_credentials| do
43
+ # assume_role_credentials.assume_role_params['token_code'] = update_token
44
+ # end
45
+ #
31
46
  def initialize(options = {})
32
47
  client_opts = {}
33
48
  @assume_role_params = {}
@@ -39,12 +54,16 @@ module Aws
39
54
  end
40
55
  end
41
56
  @client = client_opts[:client] || STS::Client.new(client_opts)
57
+ @async_refresh = true
42
58
  super
43
59
  end
44
60
 
45
61
  # @return [STS::Client]
46
62
  attr_reader :client
47
63
 
64
+ # @return [Hash]
65
+ attr_reader :assume_role_params
66
+
48
67
  private
49
68
 
50
69
  def refresh
@@ -17,7 +17,7 @@ module Aws
17
17
  # ...
18
18
  # )
19
19
  # For full list of parameters accepted
20
- # @see Aws::STS::Client#assume_role_with_web_identity
20
+ # @see Aws::STS::Client#assume_role_with_web_identity
21
21
  #
22
22
  #
23
23
  # If you omit `:client` option, a new {STS::Client} object will be
@@ -39,10 +39,16 @@ module Aws
39
39
  # encoded UUID is generated as the session name
40
40
  #
41
41
  # @option options [STS::Client] :client
42
+ #
43
+ # @option options [Callable] before_refresh Proc called before
44
+ # credentials are refreshed. `before_refresh` is called
45
+ # with an instance of this object when
46
+ # AWS credentials are required and need to be refreshed.
42
47
  def initialize(options = {})
43
48
  client_opts = {}
44
49
  @assume_role_web_identity_params = {}
45
50
  @token_file = options.delete(:web_identity_token_file)
51
+ @async_refresh = true
46
52
  options.each_pair do |key, value|
47
53
  if self.class.assume_role_web_identity_options.include?(key)
48
54
  @assume_role_web_identity_params[key] = value
@@ -262,13 +262,17 @@ module Aws
262
262
  end
263
263
 
264
264
  def convert_stub(operation_name, stub)
265
- case stub
265
+ stub = case stub
266
266
  when Proc then stub
267
267
  when Exception, Class then { error: stub }
268
268
  when String then service_error_stub(stub)
269
269
  when Hash then http_response_stub(operation_name, stub)
270
270
  else { data: stub }
271
271
  end
272
+ if Hash === stub
273
+ stub[:mutex] = Mutex.new
274
+ end
275
+ stub
272
276
  end
273
277
 
274
278
  def service_error_stub(error_code)
@@ -136,8 +136,9 @@ module Aws
136
136
 
137
137
  def fetch_token
138
138
  open_connection do |conn|
139
+ created_time = Time.now
139
140
  token_value, token_ttl = http_put(conn, @token_ttl)
140
- @token = Token.new(value: token_value, ttl: token_ttl)
141
+ @token = Token.new(value: token_value, ttl: token_ttl, created_time: created_time)
141
142
  end
142
143
  end
143
144
 
@@ -222,7 +223,7 @@ module Aws
222
223
  def initialize(options = {})
223
224
  @ttl = options[:ttl]
224
225
  @value = options[:value]
225
- @created_time = Time.now
226
+ @created_time = options[:created_time] || Time.now
226
227
  end
227
228
 
228
229
  # [String] Returns the token value.
@@ -43,6 +43,10 @@ module Aws
43
43
  # @option options [IO] :http_debug_output (nil) HTTP wire
44
44
  # traces are sent to this object. You can specify something
45
45
  # like $stdout.
46
+ # @option options [Callable] before_refresh Proc called before
47
+ # credentials are refreshed. `before_refresh` is called
48
+ # with an instance of this object when
49
+ # AWS credentials are required and need to be refreshed.
46
50
  def initialize options = {}
47
51
  @retries = options[:retries] || 5
48
52
  @ip_address = options[:ip_address] || '169.254.170.2'
@@ -58,6 +62,7 @@ module Aws
58
62
  @http_read_timeout = options[:http_read_timeout] || 5
59
63
  @http_debug_output = options[:http_debug_output]
60
64
  @backoff = backoff(options[:backoff])
65
+ @async_refresh = false
61
66
  super
62
67
  end
63
68
 
@@ -18,7 +18,7 @@ module Aws
18
18
  @code = self.class.code
19
19
  @context = context
20
20
  @data = data
21
- @message = message && !message.empty? ? message : self.class
21
+ @message = message && !message.empty? ? message : self.class.to_s
22
22
  super(@message)
23
23
  end
24
24
 
@@ -63,6 +63,10 @@ module Aws
63
63
  # @option options [Integer] :token_ttl Time-to-Live in seconds for EC2
64
64
  # Metadata Token used for fetching Metadata Profile Credentials, defaults
65
65
  # to 21600 seconds
66
+ # @option options [Callable] before_refresh Proc called before
67
+ # credentials are refreshed. `before_refresh` is called
68
+ # with an instance of this object when
69
+ # AWS credentials are required and need to be refreshed.
66
70
  def initialize(options = {})
67
71
  @retries = options[:retries] || 1
68
72
  endpoint_mode = resolve_endpoint_mode(options)
@@ -74,6 +78,8 @@ module Aws
74
78
  @backoff = backoff(options[:backoff])
75
79
  @token_ttl = options[:token_ttl] || 21_600
76
80
  @token = nil
81
+ @no_refresh_until = nil
82
+ @async_refresh = false
77
83
  super
78
84
  end
79
85
 
@@ -121,18 +127,48 @@ module Aws
121
127
  end
122
128
 
123
129
  def refresh
130
+ if @no_refresh_until && @no_refresh_until > Time.now
131
+ warn_expired_credentials
132
+ return
133
+ end
134
+
124
135
  # Retry loading credentials up to 3 times is the instance metadata
125
136
  # service is responding but is returning invalid JSON documents
126
137
  # in response to the GET profile credentials call.
127
138
  begin
128
139
  retry_errors([Aws::Json::ParseError, StandardError], max_retries: 3) do
129
140
  c = Aws::Json.load(get_credentials.to_s)
130
- @credentials = Credentials.new(
131
- c['AccessKeyId'],
132
- c['SecretAccessKey'],
133
- c['Token']
134
- )
135
- @expiration = c['Expiration'] ? Time.iso8601(c['Expiration']) : nil
141
+ if empty_credentials?(@credentials)
142
+ @credentials = Credentials.new(
143
+ c['AccessKeyId'],
144
+ c['SecretAccessKey'],
145
+ c['Token']
146
+ )
147
+ @expiration = c['Expiration'] ? Time.iso8601(c['Expiration']) : nil
148
+ if @expiration && @expiration < Time.now
149
+ @no_refresh_until = Time.now + refresh_offset
150
+ warn_expired_credentials
151
+ end
152
+ else
153
+ # credentials are already set, update them only if the new ones are not empty
154
+ if !c['AccessKeyId'] || c['AccessKeyId'].empty?
155
+ # error getting new credentials
156
+ @no_refresh_until = Time.now + refresh_offset
157
+ warn_expired_credentials
158
+ else
159
+ @credentials = Credentials.new(
160
+ c['AccessKeyId'],
161
+ c['SecretAccessKey'],
162
+ c['Token']
163
+ )
164
+ @expiration = c['Expiration'] ? Time.iso8601(c['Expiration']) : nil
165
+ if @expiration && @expiration < Time.now
166
+ @no_refresh_until = Time.now + refresh_offset
167
+ warn_expired_credentials
168
+ end
169
+ end
170
+ end
171
+
136
172
  end
137
173
  rescue Aws::Json::ParseError
138
174
  raise Aws::Errors::MetadataParserError
@@ -153,10 +189,11 @@ module Aws
153
189
  begin
154
190
  retry_errors(NETWORK_ERRORS, max_retries: @retries) do
155
191
  unless token_set?
192
+ created_time = Time.now
156
193
  token_value, ttl = http_put(
157
194
  conn, METADATA_TOKEN_PATH, @token_ttl
158
195
  )
159
- @token = Token.new(token_value, ttl) if token_value && ttl
196
+ @token = Token.new(token_value, ttl, created_time) if token_value && ttl
160
197
  end
161
198
  end
162
199
  rescue *NETWORK_ERRORS
@@ -166,9 +203,17 @@ module Aws
166
203
  end
167
204
 
168
205
  token = @token.value if token_set?
169
- metadata = http_get(conn, METADATA_PATH_BASE, token)
170
- profile_name = metadata.lines.first.strip
171
- http_get(conn, METADATA_PATH_BASE + profile_name, token)
206
+
207
+ begin
208
+ metadata = http_get(conn, METADATA_PATH_BASE, token)
209
+ profile_name = metadata.lines.first.strip
210
+ http_get(conn, METADATA_PATH_BASE + profile_name, token)
211
+ rescue TokenExpiredError
212
+ # Token has expired, reset it
213
+ # The next retry should fetch it
214
+ @token = nil
215
+ raise Non200Response
216
+ end
172
217
  end
173
218
  end
174
219
  rescue
@@ -200,9 +245,15 @@ module Aws
200
245
  headers = { 'User-Agent' => "aws-sdk-ruby3/#{CORE_GEM_VERSION}" }
201
246
  headers['x-aws-ec2-metadata-token'] = token if token
202
247
  response = connection.request(Net::HTTP::Get.new(path, headers))
203
- raise Non200Response unless response.code.to_i == 200
204
248
 
205
- response.body
249
+ case response.code.to_i
250
+ when 200
251
+ response.body
252
+ when 401
253
+ raise TokenExpiredError
254
+ else
255
+ raise Non200Response
256
+ end
206
257
  end
207
258
 
208
259
  # PUT request fetch token with ttl
@@ -241,13 +292,28 @@ module Aws
241
292
  end
242
293
  end
243
294
 
295
+ def warn_expired_credentials
296
+ warn("Attempting credential expiration extension due to a credential "\
297
+ "service availability issue. A refresh of these credentials "\
298
+ "will be attempted again in 5 minutes.")
299
+ end
300
+
301
+ def empty_credentials?(creds)
302
+ !creds || !creds.access_key_id || creds.access_key_id.empty?
303
+ end
304
+
305
+ # Compute an offset for refresh with jitter
306
+ def refresh_offset
307
+ 300 + rand(0..60)
308
+ end
309
+
244
310
  # @api private
245
311
  # Token used to fetch IMDS profile and credentials
246
312
  class Token
247
- def initialize(value, ttl)
313
+ def initialize(value, ttl, created_time = Time.now)
248
314
  @ttl = ttl
249
315
  @value = value
250
- @created_time = Time.now
316
+ @created_time = created_time
251
317
  end
252
318
 
253
319
  # [String] token value
@@ -48,11 +48,11 @@ module Aws
48
48
  #
49
49
  module PageableResponse
50
50
 
51
- def self.extended(base)
52
- base.extend Enumerable
53
- base.extend UnsafeEnumerableMethods
54
- base.instance_variable_set("@last_page", nil)
55
- base.instance_variable_set("@more_results", nil)
51
+ def self.apply(base)
52
+ base.extend Extension
53
+ base.instance_variable_set(:@last_page, nil)
54
+ base.instance_variable_set(:@more_results, nil)
55
+ base
56
56
  end
57
57
 
58
58
  # @return [Paging::Pager]
@@ -62,39 +62,26 @@ module Aws
62
62
  # when this method returns `false` will raise an error.
63
63
  # @return [Boolean]
64
64
  def last_page?
65
- if @last_page.nil?
66
- @last_page = !@pager.truncated?(self)
67
- end
68
- @last_page
65
+ # Actual implementation is in PageableResponse::Extension
69
66
  end
70
67
 
71
68
  # Returns `true` if there are more results. Calling {#next_page} will
72
69
  # return the next response.
73
70
  # @return [Boolean]
74
71
  def next_page?
75
- !last_page?
72
+ # Actual implementation is in PageableResponse::Extension
76
73
  end
77
74
 
78
75
  # @return [Seahorse::Client::Response]
79
76
  def next_page(params = {})
80
- if last_page?
81
- raise LastPageError.new(self)
82
- else
83
- next_response(params)
84
- end
77
+ # Actual implementation is in PageableResponse::Extension
85
78
  end
86
79
 
87
80
  # Yields the current and each following response to the given block.
88
81
  # @yieldparam [Response] response
89
82
  # @return [Enumerable,nil] Returns a new Enumerable if no block is given.
90
83
  def each(&block)
91
- return enum_for(:each_page) unless block_given?
92
- response = self
93
- yield(response)
94
- until response.last_page?
95
- response = response.next_page
96
- yield(response)
97
- end
84
+ # Actual implementation is in PageableResponse::Extension
98
85
  end
99
86
  alias each_page each
100
87
 
@@ -105,9 +92,7 @@ module Aws
105
92
  # @return [Seahorse::Client::Response] Returns the next page of
106
93
  # results.
107
94
  def next_response(params)
108
- params = next_page_params(params)
109
- request = context.client.build_request(context.operation_name, params)
110
- request.send_request
95
+ # Actual implementation is in PageableResponse::Extension
111
96
  end
112
97
 
113
98
  # @param [Hash] params A hash of additional request params to
@@ -115,13 +100,7 @@ module Aws
115
100
  # @return [Hash] Returns the hash of request parameters for the
116
101
  # next page, merging any given params.
117
102
  def next_page_params(params)
118
- # Remove all previous tokens from original params
119
- # Sometimes a token can be nil and merge would not include it.
120
- tokens = @pager.tokens.values.map(&:to_sym)
121
-
122
- params_without_tokens = context[:original_params].reject { |k, _v| tokens.include?(k) }
123
- params_without_tokens.merge!(@pager.next_tokens(self).merge(params))
124
- params_without_tokens
103
+ # Actual implementation is in PageableResponse::Extension
125
104
  end
126
105
 
127
106
  # Raised when calling {PageableResponse#next_page} on a pager that
@@ -168,5 +147,66 @@ module Aws
168
147
  end
169
148
 
170
149
  end
150
+
151
+ # The actual decorator module implementation. It is in a distinct module
152
+ # so that it can be used to extend objects without busting Ruby's constant cache.
153
+ # object.extend(mod) bust the constant cache only if `mod` contains constants of its own.
154
+ # @api private
155
+ module Extension
156
+
157
+ include Enumerable
158
+ include UnsafeEnumerableMethods
159
+
160
+ attr_accessor :pager
161
+
162
+ def last_page?
163
+ if @last_page.nil?
164
+ @last_page = !@pager.truncated?(self)
165
+ end
166
+ @last_page
167
+ end
168
+
169
+ def next_page?
170
+ !last_page?
171
+ end
172
+
173
+ def next_page(params = {})
174
+ if last_page?
175
+ raise LastPageError.new(self)
176
+ else
177
+ next_response(params)
178
+ end
179
+ end
180
+
181
+ def each(&block)
182
+ return enum_for(:each_page) unless block_given?
183
+ response = self
184
+ yield(response)
185
+ until response.last_page?
186
+ response = response.next_page
187
+ yield(response)
188
+ end
189
+ end
190
+ alias each_page each
191
+
192
+ private
193
+
194
+ def next_response(params)
195
+ params = next_page_params(params)
196
+ request = context.client.build_request(context.operation_name, params)
197
+ request.send_request
198
+ end
199
+
200
+ def next_page_params(params)
201
+ # Remove all previous tokens from original params
202
+ # Sometimes a token can be nil and merge would not include it.
203
+ tokens = @pager.tokens.values.map(&:to_sym)
204
+
205
+ params_without_tokens = context[:original_params].reject { |k, _v| tokens.include?(k) }
206
+ params_without_tokens.merge!(@pager.next_tokens(self).merge(params))
207
+ params_without_tokens
208
+ end
209
+
210
+ end
171
211
  end
172
212
  end