gotime_aws 2.5.6

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.
@@ -0,0 +1,296 @@
1
+ module Aws
2
+
3
+ class ResourceNotFoundError < StandardError
4
+
5
+ end
6
+
7
+ # Exception class to signal any Amazon errors. All errors occuring during calls to Amazon's
8
+ # web services raise this type of error.
9
+ # Attribute inherited by RuntimeError:
10
+ # message - the text of the error, generally as returned by AWS in its XML response.
11
+ class AwsError < RuntimeError
12
+
13
+ # either an array of errors where each item is itself an array of [code, message]),
14
+ # or an error string if the error was raised manually, as in <tt>AwsError.new('err_text')</tt>
15
+ attr_reader :errors
16
+
17
+ # Request id (if exists)
18
+ attr_reader :request_id
19
+
20
+ # Response HTTP error code
21
+ attr_reader :http_code
22
+
23
+ # Raw request text data to AWS
24
+ attr_reader :request_data
25
+
26
+ attr_reader :response
27
+
28
+ def initialize(errors=nil, http_code=nil, request_id=nil, request_data=nil, response=nil)
29
+ @errors = errors
30
+ @request_id = request_id
31
+ @http_code = http_code
32
+ @request_data = request_data
33
+ @response = response
34
+ msg = @errors.is_a?(Array) ? @errors.map { |code, m| "#{code}: #{m}" }.join("; ") : @errors.to_s
35
+ msg += "\nREQUEST=#{@request_data} " unless @request_data.nil?
36
+ msg += "\nREQUEST ID=#{@request_id} " unless @request_id.nil?
37
+ super(msg)
38
+ end
39
+
40
+ # Does any of the error messages include the regexp +pattern+?
41
+ # Used to determine whether to retry request.
42
+ def include?(pattern_or_string)
43
+ if pattern_or_string.is_a?(String)
44
+ puts 'convert to pattern'
45
+ pattern_or_string = /#{pattern_or_string}/
46
+ end
47
+ if @errors.is_a?(Array)
48
+ @errors.each { |code, msg| return true if code =~ pattern_or_string }
49
+ else
50
+ return true if @errors_str =~ pattern_or_string
51
+ end
52
+ false
53
+ end
54
+
55
+ # Generic handler for AwsErrors. +aws+ is the Aws::S3, Aws::EC2, or Aws::SQS
56
+ # object that caused the exception (it must provide last_request and last_response). Supported
57
+ # boolean options are:
58
+ # * <tt>:log</tt> print a message into the log using aws.logger to access the Logger
59
+ # * <tt>:puts</tt> do a "puts" of the error
60
+ # * <tt>:raise</tt> re-raise the error after logging
61
+ def self.on_aws_exception(aws, options={:raise=>true, :log=>true})
62
+ # Only log & notify if not user error
63
+ if !options[:raise] || system_error?($!)
64
+ error_text = "#{$!.inspect}\n#{$@}.join('\n')}"
65
+ puts error_text if options[:puts]
66
+ # Log the error
67
+ if options[:log]
68
+ request = aws.last_request ? aws.last_request.path : '-none-'
69
+ response = aws.last_response ? "#{aws.last_response.code} -- #{aws.last_response.message} -- #{aws.last_response.body}" : '-none-'
70
+ @response = response
71
+ aws.logger.error error_text
72
+ aws.logger.error "Request was: #{request}"
73
+ aws.logger.error "Response was: #{response}"
74
+ end
75
+ end
76
+ raise if options[:raise] # re-raise an exception
77
+ return nil
78
+ end
79
+
80
+ # True if e is an AWS system error, i.e. something that is for sure not the caller's fault.
81
+ # Used to force logging.
82
+ def self.system_error?(e)
83
+ !e.is_a?(self) || e.message =~ /InternalError|InsufficientInstanceCapacity|Unavailable/
84
+ end
85
+
86
+ end
87
+
88
+ # Simplified version
89
+ class AwsError2 < RuntimeError
90
+ # Request id (if exists)
91
+ attr_reader :request_id
92
+
93
+ # Response HTTP error code
94
+ attr_reader :http_code
95
+
96
+ # Raw request text data to AWS
97
+ attr_reader :request_data
98
+
99
+ attr_reader :response
100
+
101
+ attr_reader :errors
102
+
103
+ def initialize(http_code=nil, request_id=nil, request_data=nil, response=nil)
104
+
105
+ @request_id = request_id
106
+ @http_code = http_code
107
+ @request_data = request_data
108
+ @response = response
109
+ # puts '@response=' + @response.inspect
110
+
111
+ if @response
112
+ ref = XmlSimple.xml_in(@response, {"ForceArray"=>false})
113
+ # puts "refxml=" + ref.inspect
114
+ msg = "#{ref['Error']['Code']}: #{ref['Error']['Message']}"
115
+ else
116
+ msg = "#{@http_code}: REQUEST(#{@request_data})"
117
+ end
118
+ msg += "\nREQUEST ID=#{@request_id} " unless @request_id.nil?
119
+ super(msg)
120
+ end
121
+
122
+
123
+ end
124
+
125
+
126
+ class AWSErrorHandler
127
+ # 0-100 (%)
128
+ DEFAULT_CLOSE_ON_4XX_PROBABILITY = 10
129
+
130
+ @@reiteration_start_delay = 0.2
131
+
132
+ def self.reiteration_start_delay
133
+ @@reiteration_start_delay
134
+ end
135
+
136
+ def self.reiteration_start_delay=(reiteration_start_delay)
137
+ @@reiteration_start_delay = reiteration_start_delay
138
+ end
139
+
140
+ @@reiteration_time = 5
141
+
142
+ def self.reiteration_time
143
+ @@reiteration_time
144
+ end
145
+
146
+ def self.reiteration_time=(reiteration_time)
147
+ @@reiteration_time = reiteration_time
148
+ end
149
+
150
+ @@close_on_error = true
151
+
152
+ def self.close_on_error
153
+ @@close_on_error
154
+ end
155
+
156
+ def self.close_on_error=(close_on_error)
157
+ @@close_on_error = close_on_error
158
+ end
159
+
160
+ @@close_on_4xx_probability = DEFAULT_CLOSE_ON_4XX_PROBABILITY
161
+
162
+ def self.close_on_4xx_probability
163
+ @@close_on_4xx_probability
164
+ end
165
+
166
+ def self.close_on_4xx_probability=(close_on_4xx_probability)
167
+ @@close_on_4xx_probability = close_on_4xx_probability
168
+ end
169
+
170
+ # params:
171
+ # :reiteration_time
172
+ # :errors_list
173
+ # :close_on_error = true | false
174
+ # :close_on_4xx_probability = 1-100
175
+ def initialize(aws, parser, params={}) #:nodoc:
176
+ @aws = aws # Link to RightEc2 | RightSqs | RightS3 instance
177
+ @parser = parser # parser to parse Amazon response
178
+ @started_at = Time.now
179
+ @stop_at = @started_at + (params[:reiteration_time] || @@reiteration_time)
180
+ @errors_list = params[:errors_list] || []
181
+ @reiteration_delay = @@reiteration_start_delay
182
+ @retries = 0
183
+ # close current HTTP(S) connection on 5xx, errors from list and 4xx errors
184
+ @close_on_error = params[:close_on_error].nil? ? @@close_on_error : params[:close_on_error]
185
+ @close_on_4xx_probability = params[:close_on_4xx_probability] || @@close_on_4xx_probability
186
+ end
187
+
188
+ # Returns false if
189
+ def check(request, options={}) #:nodoc:
190
+ result = false
191
+ error_found = false
192
+ redirect_detected = false
193
+ error_match = nil
194
+ last_errors_text = ''
195
+ response = @aws.last_response
196
+ # log error
197
+ request_text_data = "#{request[:server]}:#{request[:port]}#{request[:request].path}"
198
+ # is this a redirect?
199
+ # yes!
200
+ if response.is_a?(Net::HTTPRedirection)
201
+ redirect_detected = true
202
+ else
203
+ # no, it's an error ...
204
+ @aws.logger.warn("##### #{@aws.class.name} returned an error: #{response.code} #{response.message}\n#{response.body} #####")
205
+ @aws.logger.warn("##### #{@aws.class.name} request: #{request_text_data} ####")
206
+ end
207
+ # Check response body: if it is an Amazon XML document or not:
208
+ if redirect_detected || (response.body && response.body[/<\?xml/]) # ... it is a xml document
209
+ @aws.class.bench_xml.add! do
210
+ error_parser = RightErrorResponseParser.new
211
+ error_parser.parse(response)
212
+ @aws.last_errors = error_parser.errors
213
+ @aws.last_request_id = error_parser.requestID
214
+ last_errors_text = @aws.last_errors.flatten.join("\n")
215
+ # on redirect :
216
+ if redirect_detected
217
+ location = response['location']
218
+ # ... log information and ...
219
+ @aws.logger.info("##### #{@aws.class.name} redirect requested: #{response.code} #{response.message} #####")
220
+ @aws.logger.info("##### New location: #{location} #####")
221
+ # ... fix the connection data
222
+ request[:server] = URI.parse(location).host
223
+ request[:protocol] = URI.parse(location).scheme
224
+ request[:port] = URI.parse(location).port
225
+ end
226
+ end
227
+ else # ... it is not a xml document(probably just a html page?)
228
+ @aws.last_errors = [[response.code, "#{response.message} (#{request_text_data})"]]
229
+ @aws.last_request_id = '-undefined-'
230
+ last_errors_text = response.message
231
+ end
232
+ # now - check the error
233
+ unless redirect_detected
234
+ @errors_list.each do |error_to_find|
235
+ if last_errors_text[/#{error_to_find}/i]
236
+ error_found = true
237
+ error_match = error_to_find
238
+ @aws.logger.warn("##### Retry is needed, error pattern match: #{error_to_find} #####")
239
+ break
240
+ end
241
+ end
242
+ end
243
+ # check the time has gone from the first error come
244
+ if redirect_detected || error_found
245
+ # Close the connection to the server and recreate a new one.
246
+ # It may have a chance that one server is a semi-down and reconnection
247
+ # will help us to connect to the other server
248
+ if !redirect_detected && @close_on_error
249
+ @aws.connection.finish "#{self.class.name}: error match to pattern '#{error_match}'"
250
+ end
251
+ # puts 'OPTIONS3=' + options.inspect
252
+ if options[:retries].nil? || @retries < options[:retries]
253
+ if (Time.now < @stop_at)
254
+ @retries += 1
255
+ unless redirect_detected
256
+ @aws.logger.warn("##### Retry ##{@retries} is being performed. Sleeping for #{@reiteration_delay} sec. Whole time: #{Time.now-@started_at} sec ####")
257
+ sleep @reiteration_delay
258
+ @reiteration_delay *= 2
259
+
260
+ # Always make sure that the fp is set to point to the beginning(?)
261
+ # of the File/IO. TODO: it assumes that offset is 0, which is bad.
262
+ if (request[:request].body_stream && request[:request].body_stream.respond_to?(:pos))
263
+ begin
264
+ request[:request].body_stream.pos = 0
265
+ rescue Exception => e
266
+ @logger.warn("Retry may fail due to unable to reset the file pointer" +
267
+ " -- #{self.class.name} : #{e.inspect}")
268
+ end
269
+ end
270
+ else
271
+ @aws.logger.info("##### Retry ##{@retries} is being performed due to a redirect. ####")
272
+ end
273
+ result = @aws.request_info(request, @parser, options)
274
+ else
275
+ @aws.logger.warn("##### Ooops, time is over... ####")
276
+ end
277
+ else
278
+ @aws.logger.info("##### Stopped retrying because retries=#{@retries} and max=#{options[:retries]} ####")
279
+ end
280
+ # aha, this is unhandled error:
281
+ elsif @close_on_error
282
+ # Is this a 5xx error ?
283
+ if @aws.last_response.code.to_s[/^5\d\d$/]
284
+ @aws.connection.finish "#{self.class.name}: code: #{@aws.last_response.code}: '#{@aws.last_response.message}'"
285
+ # Is this a 4xx error ?
286
+ elsif @aws.last_response.code.to_s[/^4\d\d$/] && @close_on_4xx_probability > rand(100)
287
+ @aws.connection.finish "#{self.class.name}: code: #{@aws.last_response.code}: '#{@aws.last_response.message}', " +
288
+ "probability: #{@close_on_4xx_probability}%"
289
+ end
290
+ end
291
+ result
292
+ end
293
+
294
+ end
295
+
296
+ end
@@ -0,0 +1,227 @@
1
+ module Aws
2
+
3
+ #-----------------------------------------------------------------
4
+
5
+ class RightSaxParserCallback #:nodoc:
6
+ def self.include_callback
7
+ include XML::SaxParser::Callbacks
8
+ end
9
+
10
+ def initialize(right_aws_parser)
11
+ @right_aws_parser = right_aws_parser
12
+ end
13
+
14
+ def on_start_element(name, attr_hash)
15
+ @right_aws_parser.tag_start(name, attr_hash)
16
+ end
17
+
18
+ def on_characters(chars)
19
+ @right_aws_parser.text(chars)
20
+ end
21
+
22
+ def on_end_element(name)
23
+ @right_aws_parser.tag_end(name)
24
+ end
25
+
26
+ def on_start_document;
27
+ end
28
+
29
+ def on_comment(msg)
30
+ ;
31
+ end
32
+
33
+ def on_processing_instruction(target, data)
34
+ ;
35
+ end
36
+
37
+ def on_cdata_block(cdata)
38
+ ;
39
+ end
40
+
41
+ def on_end_document;
42
+ end
43
+ end
44
+
45
+ class AwsParser #:nodoc:
46
+ # default parsing library
47
+ DEFAULT_XML_LIBRARY = 'rexml'
48
+ # a list of supported parsers
49
+ @@supported_xml_libs = [DEFAULT_XML_LIBRARY, 'libxml']
50
+
51
+ @@xml_lib = DEFAULT_XML_LIBRARY # xml library name: 'rexml' | 'libxml'
52
+ def self.xml_lib
53
+ @@xml_lib
54
+ end
55
+
56
+ def self.xml_lib=(new_lib_name)
57
+ @@xml_lib = new_lib_name
58
+ end
59
+
60
+ attr_accessor :result
61
+ attr_reader :xmlpath
62
+ attr_accessor :xml_lib
63
+
64
+ def initialize(params={})
65
+ @xmlpath = ''
66
+ @result = false
67
+ @text = ''
68
+ @xml_lib = params[:xml_lib] || @@xml_lib
69
+ @logger = params[:logger]
70
+ reset
71
+ end
72
+
73
+ def tag_start(name, attributes)
74
+ @text = ''
75
+ tagstart(name, attributes)
76
+ @xmlpath += @xmlpath.empty? ? name : "/#{name}"
77
+ end
78
+
79
+ def tag_end(name)
80
+ if @xmlpath =~ /^(.*?)\/?#{name}$/
81
+ @xmlpath = $1
82
+ end
83
+ tagend(name)
84
+ end
85
+
86
+ def text(text)
87
+ @text += text
88
+ tagtext(text)
89
+ end
90
+
91
+ # Parser method.
92
+ # Params:
93
+ # xml_text - xml message text(String) or Net:HTTPxxx instance (response)
94
+ # params[:xml_lib] - library name: 'rexml' | 'libxml'
95
+ def parse(xml_text, params={})
96
+ # Get response body
97
+ unless xml_text.is_a?(String)
98
+ xml_text = xml_text.body.respond_to?(:force_encoding) ? xml_text.body.force_encoding("UTF-8") : xml_text.body
99
+ end
100
+
101
+ @xml_lib = params[:xml_lib] || @xml_lib
102
+ # check that we had no problems with this library otherwise use default
103
+ @xml_lib = DEFAULT_XML_LIBRARY unless @@supported_xml_libs.include?(@xml_lib)
104
+ # load xml library
105
+ if @xml_lib=='libxml' && !defined?(XML::SaxParser)
106
+ begin
107
+ require 'xml/libxml'
108
+ # is it new ? - Setup SaxParserCallback
109
+ if XML::Parser::VERSION >= '0.5.1.0'
110
+ RightSaxParserCallback.include_callback
111
+ end
112
+ rescue LoadError => e
113
+ @@supported_xml_libs.delete(@xml_lib)
114
+ @xml_lib = DEFAULT_XML_LIBRARY
115
+ if @logger
116
+ @logger.error e.inspect
117
+ @logger.error e.backtrace
118
+ @logger.info "Can not load 'libxml' library. '#{DEFAULT_XML_LIBRARY}' is used for parsing."
119
+ end
120
+ end
121
+ end
122
+ # Parse the xml text
123
+ case @xml_lib
124
+ when 'libxml'
125
+ xml = XML::SaxParser.string(xml_text)
126
+ # check libxml-ruby version
127
+ if XML::Parser::VERSION >= '0.5.1.0'
128
+ xml.callbacks = RightSaxParserCallback.new(self)
129
+ else
130
+ xml.on_start_element { |name, attr_hash| self.tag_start(name, attr_hash) }
131
+ xml.on_characters { |text| self.text(text) }
132
+ xml.on_end_element { |name| self.tag_end(name) }
133
+ end
134
+ xml.parse
135
+ else
136
+ REXML::Document.parse_stream(xml_text, self)
137
+ end
138
+ end
139
+
140
+ # Parser must have a lots of methods
141
+ # (see /usr/lib/ruby/1.8/rexml/parsers/streamparser.rb)
142
+ # We dont need most of them in AwsParser and method_missing helps us
143
+ # to skip their definition
144
+ def method_missing(method, *params)
145
+ # if the method is one of known - just skip it ...
146
+ return if [:comment, :attlistdecl, :notationdecl, :elementdecl,
147
+ :entitydecl, :cdata, :xmldecl, :attlistdecl, :instruction,
148
+ :doctype].include?(method)
149
+ # ... else - call super to raise an exception
150
+ super(method, params)
151
+ end
152
+
153
+ # the functions to be overriden by children (if nessesery)
154
+ def reset;
155
+ end
156
+
157
+ def tagstart(name, attributes)
158
+ ;
159
+ end
160
+
161
+ def tagend(name)
162
+ ;
163
+ end
164
+
165
+ def tagtext(text)
166
+ ;
167
+ end
168
+ end
169
+
170
+ #-----------------------------------------------------------------
171
+ # PARSERS: Errors
172
+ #-----------------------------------------------------------------
173
+
174
+ #<Error>
175
+ # <Code>TemporaryRedirect</Code>
176
+ # <Message>Please re-send this request to the specified temporary endpoint. Continue to use the original request endpoint for future requests.</Message>
177
+ # <RequestId>FD8D5026D1C5ABA3</RequestId>
178
+ # <Endpoint>bucket-for-k.s3-external-3.amazonaws.com</Endpoint>
179
+ # <HostId>ItJy8xPFPli1fq/JR3DzQd3iDvFCRqi1LTRmunEdM1Uf6ZtW2r2kfGPWhRE1vtaU</HostId>
180
+ # <Bucket>bucket-for-k</Bucket>
181
+ #</Error>
182
+
183
+ class RightErrorResponseParser < AwsParser #:nodoc:
184
+ attr_accessor :errors # array of hashes: error/message
185
+ attr_accessor :requestID
186
+ # attr_accessor :endpoint, :host_id, :bucket
187
+ def parse(response)
188
+ super
189
+ end
190
+
191
+ def tagend(name)
192
+ case name
193
+ when 'RequestID';
194
+ @requestID = @text
195
+ when 'Code';
196
+ @code = @text
197
+ when 'Message';
198
+ @message = @text
199
+ # when 'Endpoint' ; @endpoint = @text
200
+ # when 'HostId' ; @host_id = @text
201
+ # when 'Bucket' ; @bucket = @text
202
+ when 'Error';
203
+ @errors << [@code, @message]
204
+ end
205
+ end
206
+
207
+ def reset
208
+ @errors = []
209
+ end
210
+ end
211
+
212
+ # Dummy parser - does nothing
213
+ # Returns the original params back
214
+ class RightDummyParser # :nodoc:
215
+ attr_accessor :result
216
+
217
+ def parse(response, params={})
218
+ @result = [response, params]
219
+ end
220
+ end
221
+
222
+ class RightHttp2xxParser < AwsParser # :nodoc:
223
+ def parse(response)
224
+ @result = response.is_a?(Net::HTTPSuccess)
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,20 @@
1
+ # require_relative was introduced in 1.9.2. This makes it
2
+ # available to younger rubies. It trys hard to not re-require
3
+ # files.
4
+
5
+ unless Kernel.respond_to?(:require_relative)
6
+ module Kernel
7
+ def require_relative(path)
8
+ desired_path = File.expand_path('../' + path.to_str, caller[0])
9
+ shortest = desired_path
10
+ $:.each do |path|
11
+ path += '/'
12
+ if desired_path.index(path) == 0
13
+ candidate = desired_path.sub(path, '')
14
+ shortest = candidate if candidate.size < shortest.size
15
+ end
16
+ end
17
+ require shortest
18
+ end
19
+ end
20
+ end