aws-sdk-core 2.0.18 → 2.0.19

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/apis/CloudFormation.resources.json +55 -20
  3. data/apis/CloudFront.waiters.json +35 -17
  4. data/apis/DynamoDB.waiters.json +27 -17
  5. data/apis/EC2.api.json +5 -0
  6. data/apis/EC2.resources.json +449 -319
  7. data/apis/EC2.waiters.json +389 -139
  8. data/apis/{EMR.waiters2.json → EMR.waiters.json} +0 -0
  9. data/apis/ElasticTranscoder.api.json +34 -3
  10. data/apis/ElasticTranscoder.waiters.json +22 -8
  11. data/apis/Glacier.resources.json +215 -117
  12. data/apis/Glacier.waiters.json +31 -15
  13. data/apis/IAM.resources.json +380 -169
  14. data/apis/{Kinesis.waiters2.json → Kinesis.waiters.json} +0 -0
  15. data/apis/OpsWorks.resources.json +51 -17
  16. data/apis/RDS.waiters.json +87 -26
  17. data/apis/Redshift.waiters.json +54 -29
  18. data/apis/S3.resources.json +404 -143
  19. data/apis/S3.waiters.json +47 -16
  20. data/apis/SES.waiters.json +11 -7
  21. data/apis/SNS.resources.json +67 -33
  22. data/apis/SQS.resources.json +57 -30
  23. data/lib/aws-sdk-core.rb +1 -0
  24. data/lib/aws-sdk-core/api/documenter.rb +4 -3
  25. data/lib/aws-sdk-core/api/service_customizations.rb +0 -6
  26. data/lib/aws-sdk-core/client_waiters.rb +65 -50
  27. data/lib/aws-sdk-core/emr.rb +1 -0
  28. data/lib/aws-sdk-core/errors.rb +4 -0
  29. data/lib/aws-sdk-core/instance_profile_credentials.rb +1 -0
  30. data/lib/aws-sdk-core/kinesis.rb +1 -0
  31. data/lib/aws-sdk-core/paging/pager.rb +5 -1
  32. data/lib/aws-sdk-core/refreshing_credentials.rb +2 -0
  33. data/lib/aws-sdk-core/version.rb +1 -1
  34. data/lib/aws-sdk-core/waiters/errors.rb +50 -12
  35. data/lib/aws-sdk-core/waiters/poller.rb +105 -0
  36. data/lib/aws-sdk-core/waiters/provider.rb +11 -30
  37. data/lib/aws-sdk-core/waiters/waiter.rb +42 -102
  38. metadata +5 -12
  39. data/apis/CloudFront.waiters2.json +0 -47
  40. data/apis/DynamoDB.waiters2.json +0 -35
  41. data/apis/EC2.waiters2.json +0 -341
  42. data/apis/ElasticTranscoder.waiters2.json +0 -30
  43. data/apis/RDS.waiters2.json +0 -97
  44. data/apis/Redshift.waiters2.json +0 -97
  45. data/apis/S3.waiters2.json +0 -63
  46. data/apis/SES.waiters2.json +0 -18
@@ -182,6 +182,7 @@ module Aws
182
182
  end
183
183
 
184
184
  module Waiters
185
+ autoload :Poller, 'aws-sdk-core/waiters/poller'
185
186
  autoload :Errors, 'aws-sdk-core/waiters/errors'
186
187
  autoload :NullProvider, 'aws-sdk-core/waiters/null_provider'
187
188
  autoload :Provider, 'aws-sdk-core/waiters/provider'
@@ -175,15 +175,16 @@ Constructs an API client.
175
175
  m.docstring = YARD::Registry['Aws::ClientWaiters#wait_until'].docstring
176
176
 
177
177
  waiters = @client_class.waiters.waiter_names.sort.inject('') do |w,name|
178
- operation = @client_class.waiters.waiter(name).send(:operation_name)
179
- w << "<tr><td><tt>:#{name}</tt></td><td>{##{operation}}</td></tr>"
178
+ waiter = @client_class.waiters.waiter(name)
179
+ operation = waiter.poller.operation_name
180
+ w << "<tr><td><tt>:#{name}</tt></td><td>{##{operation}}</td><td>#{waiter.delay}</td><td>#{waiter.max_attempts}</td></tr>"
180
181
  end
181
182
  docstring = <<-DOCSTRING
182
183
  Returns the list of supported waiters. The following table lists the supported
183
184
  waiters and the client method they call:
184
185
  <table>
185
186
  <thead>
186
- <tr><th>Waiter Name</th><th>Client Method</th></tr>
187
+ <tr><th>Waiter Name</th><th>Client Method</th><th>Delay</th><th>Max Attempts</th></tr>
187
188
  </thead>
188
189
  <tbody>
189
190
  #{waiters}
@@ -114,12 +114,6 @@ module Aws
114
114
  # parsing of the output
115
115
  client_class.api.operation(:get_bucket_location).
116
116
  instance_variable_set("@output", nil)
117
-
118
- defs = client_class.waiters.instance_variable_get("@definitions")
119
- defs[:bucket_exists]['ignore_errors'] = ['NotFound']
120
- defs[:object_exists]['ignore_errors'] = ['NotFound']
121
- defs[:bucket_not_exists]['success_value'] = 'NotFound'
122
- defs[:object_not_exists]['success_value'] = 'NotFound'
123
117
  end
124
118
 
125
119
  customize 'sqs' do
@@ -23,76 +23,91 @@ module Aws
23
23
  end
24
24
  end
25
25
 
26
- # Waits until a particular condition is satisfied. This works by
27
- # polling a client request and checking for particular response
28
- # data or errors. Waiters each have a default duration max attempts
29
- # which are configurable. Additionally, you can register callbacks
30
- # and stop waiters by throwing `:success` or `:failure`.
31
- #
32
- # @example Basic usage
33
- # client.wait_until(:waiter_name)
34
- #
35
- # @example Configuring interval and maximum attempts
36
- # client.wait_until(:waiter_name) do |w|
37
- # w.interval = 10 # number of seconds to sleep between attempts
38
- # w.max_attempts = 6 # maximum number of polling attempts
39
- # end
26
+ # Waiters polls an API operation until a resource enters a desired
27
+ # state.
40
28
  #
41
- # @example Rescuing a failed wait
42
- # begin
43
- # client.wait_until(:waiter_name)
44
- # rescue Aws::Waiters::Errors::WaiterFailed
45
- # # gave up waiting
46
- # end
29
+ # ## Basic Usage
47
30
  #
48
- # @example Waiting with progress callbacks
49
- # client.wait_until(:waiter_name) do |w|
31
+ # Waiters will poll until they are succesful, they fail by
32
+ # entering a terminal state, or until a maximum number of attempts
33
+ # are made.
50
34
  #
51
- # # yields just before polling for change
52
- # w.before_attempt do |attempt|
53
- # # attempts - number of previous attempts made
54
- # end
35
+ # # polls in a loop, sleeping between attempts
36
+ # client.waiter_until(waiter_name, params)
37
+ #
38
+ # ## Configuration
55
39
  #
56
- # # yields before sleeping
57
- # w.before_wait do |attempt, prev_response|
58
- # # attempts - number of previous attempts made
59
- # # prev_response - the last client response received
40
+ # You can configure the maximum number of polling attempts, and the
41
+ # delay (in seconds) between each polling attempt. You configure
42
+ # waiters by passing a block to #{wait_until}:
43
+ #
44
+ # # poll for ~25 seconds
45
+ # client.wait_until(...) do |w|
46
+ # w.max_attempts = 5
47
+ # w.delay = 5
60
48
  # end
61
- # end
62
49
  #
63
- # @example Throw :success or :failure to terminate early
64
- # # wait for an hour, not for a number of requests
65
- # client.wait_until(:waiter_name) do |waiter|
66
- # one_hour = Time.now + 3600
67
- # waiter.max_attempts = nil
68
- # waiter.before_attempt do |attempt|
69
- # throw(:failure, 'waited to long') if Time.now > one_hour
50
+ # ## Callbacks
51
+ #
52
+ # You can be notified before each polling attempt and before each
53
+ # delay. If you throw `:success` or `:failure` from these callbacks,
54
+ # it will terminate the waiter.
55
+ #
56
+ # started_at = Time.now
57
+ # client.wait_until(...) do |w|
58
+ #
59
+ # # disable max attempts
60
+ # w.max_attempts = nil
61
+ #
62
+ # # poll for 1 hour, instead of a number of attempts
63
+ # before_wait do |attempts, response|
64
+ # throw :failure if Time.now - started_at > 3600
65
+ # end
66
+ #
70
67
  # end
68
+ #
69
+ # ## Handling Errors
70
+ #
71
+ # When a waiter is successful, it returns `true`. When a waiter
72
+ # fails, it raises an error. **All errors raised extend from
73
+ # {Aws::Waiters::Errors::WaiterFailed}**.
74
+ #
75
+ # begin
76
+ # client.wait_until(...)
77
+ # rescue Aws::Waiters::Errors::WaiterFailed
78
+ # # resource did not enter the desired state in time
71
79
  # end
72
80
  #
73
81
  # @param [Symbol] waiter_name The name of the waiter. See {#waiter_names}
74
82
  # for a full list of supported waiters.
83
+ #
75
84
  # @param [Hash] params Additional request parameters. See the {#waiter_names}
76
85
  # for a list of supported waiters and what request they call. The
77
86
  # called request determines the list of accepted parameters.
78
- # @return [Seahorse::Client::Response] Returns the client response from
79
- # the successful polling request. If `:success` is thrown from a callback,
80
- # then the 2nd argument to `#throw` is returned.
81
- # @yieldparam [Waiters::Waiter] waiter Yields a {Waiters::Waiter Waiter}
87
+ #
88
+ # @yieldparam [Waiters::Waiter] waiter Yields a {Waiters::Waiter Waiter}
82
89
  # object that can be configured prior to waiting.
83
- # @raise [Waiters::Errors::NoSuchWaiter] Raised when the named waiter
84
- # is not defined.
85
- # @raise [Waiters::Errors::WaiterFailed] Raised when one of the
86
- # following conditions is met:
87
90
  #
88
- # * A failure condition is detected
89
- # * The maximum number of attempts has been made without success
90
- # * `:failure` is thrown from a callback
91
+ # @raise [Errors::FailureStateError] Raised when the waiter terminates
92
+ # because the waiter has entered a state that it will not transition
93
+ # out of, preventing success.
94
+ #
95
+ # @raise [Errors::TooManyAttemptsError] Raised when the configured
96
+ # maximum number of attempts have been made, and the waiter is not
97
+ # yet successful.
98
+ #
99
+ # @raise [Errors::UnexpectedError] Raised when an error is encounted
100
+ # while polling for a resource that is not expected.
101
+ #
102
+ # @raise [Errors::NoSuchWaiterError] Raised when you request to wait
103
+ # for an unknown state.
104
+ #
105
+ # @return [Boolean] Returns `true` if the waiter was successful.
91
106
  #
92
107
  def wait_until(waiter_name, params = {}, &block)
93
108
  waiter = self.class.waiters.waiter(waiter_name)
94
109
  yield(waiter) if block_given?
95
- waiter.wait(self, params)
110
+ waiter.wait(client:self, params:params)
96
111
  end
97
112
 
98
113
  # Returns the list of supported waiters.
@@ -2,4 +2,5 @@ Aws.add_service(:EMR, {
2
2
  api: File.join(Aws::API_DIR, 'EMR.api.json'),
3
3
  docs: File.join(Aws::API_DIR, 'EMR.docs.json'),
4
4
  paginators: File.join(Aws::API_DIR, 'EMR.paginators.json'),
5
+ waiters: File.join(Aws::API_DIR, 'EMR.waiters.json'),
5
6
  })
@@ -14,10 +14,14 @@ module Aws
14
14
  # @param [Seahorse::Client::RequestContext] context
15
15
  # @param [String] message
16
16
  def initialize(context, message)
17
+ @code = self.class.code
17
18
  @context = context
18
19
  super(message)
19
20
  end
20
21
 
22
+ # @return [String]
23
+ attr_reader :code
24
+
21
25
  # @return [Seahorse::Client::RequestContext] The context of the request
22
26
  # that triggered the remote service to return this error.
23
27
  attr_reader :context
@@ -17,6 +17,7 @@ module Aws
17
17
  FAILURES = [
18
18
  Errno::EHOSTUNREACH,
19
19
  Errno::ECONNREFUSED,
20
+ Errno::EHOSTDOWN,
20
21
  SocketError,
21
22
  Timeout::Error,
22
23
  Non200Response,
@@ -2,4 +2,5 @@ Aws.add_service(:Kinesis, {
2
2
  api: File.join(Aws::API_DIR, 'Kinesis.api.json'),
3
3
  docs: File.join(Aws::API_DIR, 'Kinesis.docs.json'),
4
4
  paginators: File.join(Aws::API_DIR, 'Kinesis.paginators.json'),
5
+ waiters: File.join(Aws::API_DIR, 'Kinesis.waiters.json'),
5
6
  })
@@ -20,7 +20,7 @@ module Aws
20
20
  def next_tokens(response)
21
21
  @tokens.each.with_object({}) do |(source, target), next_tokens|
22
22
  value = JMESPath.search(source, response.data)
23
- next_tokens[target.to_sym] = value unless value.nil?
23
+ next_tokens[target.to_sym] = value unless empty_value?(value)
24
24
  end
25
25
  end
26
26
 
@@ -51,6 +51,10 @@ module Aws
51
51
  gsub(/\w+/) { |part| Seahorse::Util.underscore(part) }
52
52
  end
53
53
 
54
+ def empty_value?(value)
55
+ value.nil? || value == [] || value == {}
56
+ end
57
+
54
58
  end
55
59
  end
56
60
  end
@@ -66,6 +66,8 @@ module Aws
66
66
  if @expiration
67
67
  # are we within 5 minutes of expiration?
68
68
  (Time.now.to_i + 5 * 60) > @expiration.to_i
69
+ else
70
+ true
69
71
  end
70
72
  end
71
73
 
@@ -1,3 +1,3 @@
1
1
  module Aws
2
- VERSION = '2.0.18'
2
+ VERSION = '2.0.19'
3
3
  end
@@ -6,23 +6,61 @@ module Aws
6
6
  # succeed.
7
7
  class WaiterFailed < StandardError; end
8
8
 
9
+ class FailureStateError < WaiterFailed
10
+
11
+ MSG = "stopped waiting, encountered a failure state"
12
+
13
+ def initialize(response)
14
+ @response = response
15
+ super(MSG)
16
+ end
17
+
18
+ # @return [Seahorse::Client::Response] The response that matched
19
+ # the failure state.
20
+ attr_reader :response
21
+
22
+ end
23
+
24
+ class TooManyAttemptsError < WaiterFailed
25
+
26
+ MSG = "stopped waiting after %d attempts without success"
27
+
28
+ def initialize(attempts)
29
+ @attempts = attempts
30
+ super(MSG % [attempts])
31
+ end
32
+
33
+ # @return [Integer]
34
+ attr_reader :attempts
35
+
36
+ end
37
+
38
+ class UnexpectedError < WaiterFailed
39
+
40
+ MSG = "stopped waiting due to an unexpected error: %s"
41
+
42
+ def initialize(error)
43
+ @error = error
44
+ super(MSG % [error.message])
45
+ end
46
+
47
+ # @return [Exception] The unexpected error.
48
+ attr_reader :error
49
+
50
+ end
51
+
9
52
  # Raised when attempting to get a waiter by name and the waiter has not
10
53
  # been defined.
11
- class NoSuchWaiter < ArgumentError
54
+ class NoSuchWaiterError < ArgumentError
55
+
56
+ MSG = "no such waiter %s; valid waiter names are: %s"
57
+
12
58
  def initialize(waiter_name, waiter_names)
13
- msg = "no definition found for #{waiter_name.inspect}"
14
- msg << "; valid waiter names are:"
15
- waiter_names.sort.each.with_index do |name, n|
16
- if n % 3 == 0
17
- msg << "\n #{name.inspect}"
18
- else
19
- msg << ", #{name.inspect}"
20
- end
21
- end
22
- super(msg)
59
+ waiter_names = waiter_names.map(&:inspect).join(', ')
60
+ super(MSG % [waiter_name.inspect, waiter_names])
23
61
  end
24
- end
25
62
 
63
+ end
26
64
  end
27
65
  end
28
66
  end
@@ -0,0 +1,105 @@
1
+ module Aws
2
+ module Waiters
3
+
4
+ # Polls a single API operation inspecting the response data and/or error
5
+ # for states matching one of its acceptors.
6
+ # @api private
7
+ class Poller
8
+
9
+ # @api private
10
+ RAISE_HANDLER = Seahorse::Client::Plugins::RaiseResponseErrors::Handler
11
+
12
+ # @api private
13
+ def initialize(options = {})
14
+ @operation_name = underscore(options['operation']).to_sym
15
+ @acceptors = options['acceptors'] || []
16
+ end
17
+
18
+ # @return [Symbol]
19
+ attr_reader :operation_name
20
+
21
+ # Makes an API call, returning the resultant state and the response.
22
+ #
23
+ # * `:success` - A success state has been matched.
24
+ # * `:failure` - A terminate failure state has been matched.
25
+ # * `:retry` - The waiter may be retried.
26
+ # * `:error` - The waiter encountered an un-expected error.
27
+ #
28
+ # @example A trival (bad) example of a waiter that polls indefinetly.
29
+ #
30
+ # loop do
31
+ #
32
+ # state, resp = poller.call(client:client, params:{})
33
+ #
34
+ # case state
35
+ # when :success then return true
36
+ # when :failure then return false
37
+ # when :retry then next
38
+ # when :error then raise 'oops'
39
+ # end
40
+ #
41
+ # end
42
+ #
43
+ # @option options [required,Client] :client
44
+ # @option options [required,Hash] :params
45
+ # @return [Array<Symbol,Response>]
46
+ def call(options = {})
47
+ response = send_request(options)
48
+ @acceptors.each do |acceptor|
49
+ if acceptor_matches?(acceptor, response)
50
+ return [acceptor['state'].to_sym, response]
51
+ end
52
+ end
53
+ [response.error ? :error : :retry, response]
54
+ end
55
+
56
+ private
57
+
58
+ def send_request(options)
59
+ req = options[:client].build_request(@operation_name, options[:params])
60
+ req.handlers.remove(RAISE_HANDLER)
61
+ req.send_request
62
+ end
63
+
64
+ def acceptor_matches?(acceptor, response)
65
+ send("matches_#{acceptor['matcher']}?", acceptor, response)
66
+ end
67
+
68
+ def matches_path?(acceptor, response)
69
+ JMESPath.search(path(acceptor), response.data) == acceptor['expected']
70
+ end
71
+
72
+ def matches_pathAll?(acceptor, response)
73
+ values = JMESPath.search(path(acceptor), response.data)
74
+ Array === values &&
75
+ values.count > 0 &&
76
+ values.all? { |value| value == acceptor['expected'] }
77
+ end
78
+
79
+ def matches_pathAny?(acceptor, response)
80
+ values = JMESPath.search(path(acceptor), response.data)
81
+ Array === values &&
82
+ values.count > 0 &&
83
+ values.any? { |value| value == acceptor['expected'] }
84
+ end
85
+
86
+ def matches_status?(acceptor, response)
87
+ response.context.http_response.status_code == acceptor['expected']
88
+ end
89
+
90
+ def matches_error?(acceptor, response)
91
+ Aws::Errors::ServiceError === response.error &&
92
+ response.error.code == acceptor['expected']
93
+ end
94
+
95
+ def path(acceptor)
96
+ acceptor['argument'].gsub(/\w+/) { |s| Seahorse::Util.underscore(s) }
97
+ end
98
+
99
+ def underscore(str)
100
+ Seahorse::Util.underscore(str)
101
+ end
102
+
103
+ end
104
+ end
105
+ end
@@ -1,54 +1,35 @@
1
- require 'set'
2
-
3
1
  module Aws
4
2
  module Waiters
5
3
  # @api private
6
4
  class Provider
7
5
 
8
6
  def initialize(definitions)
9
- @waiter_names = Set.new
10
- @definitions = definitions['waiters'].each.with_object({}) do |(k,v),h|
11
- if k.match(/^__/)
12
- h[k] = v
13
- else
14
- underscore(k).to_sym.tap do |name|
15
- @waiter_names << name
16
- h[name] = v
17
- end
18
- end
7
+ @waiters = {}
8
+ definitions['waiters'].each do |waiter_name, definition|
9
+ @waiters[Seahorse::Util.underscore(waiter_name).to_sym] = {
10
+ poller: Poller.new(definition),
11
+ max_attempts: definition['maxAttempts'],
12
+ delay: definition['delay'],
13
+ }
19
14
  end
20
15
  end
21
16
 
22
17
  # @return [Array<Symbol>]
23
18
  def waiter_names
24
- @waiter_names.to_a
19
+ @waiters.keys
25
20
  end
26
21
 
27
22
  # @param [Symbol] waiter_name
28
23
  # @return [Waiter]
29
24
  # @raise [ArgumentError]
30
25
  def waiter(waiter_name)
31
- if @waiter_names.include?(waiter_name)
32
- Waiter.new(resolve('extends' => waiter_name))
33
- else
34
- raise Errors::NoSuchWaiter.new(waiter_name, waiter_names)
35
- end
36
- end
37
-
38
- private
39
-
40
- def resolve(definition)
41
- if extends = definition.delete('extends')
42
- resolve(@definitions[extends].merge(definition))
26
+ if @waiters.key?(waiter_name)
27
+ Waiter.new(@waiters[waiter_name])
43
28
  else
44
- (@definitions['__default__'] || {}).merge(definition)
29
+ raise Errors::NoSuchWaiterError.new(waiter_name, waiter_names)
45
30
  end
46
31
  end
47
32
 
48
- def underscore(str)
49
- str.gsub(/\w+/) { |part| Seahorse::Util.underscore(part) }
50
- end
51
-
52
33
  end
53
34
  end
54
35
  end