right_develop 3.1.9 → 3.1.10

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
  SHA1:
3
- metadata.gz: f9bea4294ac48a9bb1c124143e96594b3993867c
4
- data.tar.gz: 1817c459f9d80fea412f1e1e7a66c011c6629f0d
3
+ metadata.gz: ca1ee47286ed9116bc3e025b1596019d85b1b59c
4
+ data.tar.gz: 524daf75dc03226c0fa94d0c265dc494f24e7ac2
5
5
  SHA512:
6
- metadata.gz: 814de826f973fd7678c8723ea686bebc140aa18e39497e7d77d683c208dc6dddaa8895ab0bf121400c0005862ba01b49806c2ee9462863d4be55d27b71d20b5c
7
- data.tar.gz: 65416ad1cbe5429b1e74b842449b739992f9d1e2d32e53569c209932f6ab8204e43c1596e576ff290d7995d15d393e9bfc62d3057b50bb4d28d7691bb8443e7e
6
+ metadata.gz: 2264a0ad0de1f8cef1de281cca8d9ab688c3d2d14006d5a4225decb2cf7fd346cb67226832abee3933f43b65b9a5f157b5a121159d90591223a40b2ed05463a3
7
+ data.tar.gz: c00c17b93fb059d26181ebc335f25fcbecee4e8c7f0d0e7e6542a179b05a47ced720c0973e7ec9d8dc56ad1bf6a0e1801128b74b2afdfadcdcc07a70069251cc
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.1.9
1
+ 3.1.10
@@ -89,7 +89,7 @@ module RightDevelop::Testing::Client::Rest::Request
89
89
  end
90
90
 
91
91
  RETRY_DELAY = 0.5
92
- MAX_RETRIES = 100 # = 50 seconds; a socket usually times out in 60-120 seconds
92
+ MAX_RETRIES = 240 # = 120 seconds; a socket usually times out in 60-120 seconds
93
93
 
94
94
  # Overrides transmit to catch halt thrown by log_request.
95
95
  #
@@ -176,7 +176,6 @@ module RightDevelop::Testing::Client::Rest::Request
176
176
  # allowed due to multithreaded requests causing the epoch to advance
177
177
  # (in a non-throttled playback) before all requests for a past epoch have
178
178
  # been made. the current epoch is always preferred over past.
179
- logger.debug("BEGIN playback state = #{state.inspect}") if logger.debug?
180
179
  file_path = nil
181
180
  past_epochs = state[:past_epochs] ||= []
182
181
  try_epochs = [state[:epoch]] + past_epochs
@@ -203,11 +202,20 @@ module RightDevelop::Testing::Client::Rest::Request
203
202
  response_hash[:body])
204
203
  result = FakeNetHttpResponse.new(response_hash, response_metadata)
205
204
  else
206
- msg = 'Unable to locate response file(s) in epoch range ' +
207
- "[#{first_tried_epoch} - #{last_tried_epoch}]:\n " +
208
- first_tried_path.inspect
205
+ msg = <<EOF
206
+ Unable to locate response file(s) in epoch range [#{first_tried_epoch} - #{last_tried_epoch}]:
207
+ #{first_tried_path.inspect}
208
+ request checksum_data = #{request_metadata.checksum_data.inspect}
209
+ state = #{state.inspect}
210
+ EOF
209
211
  raise PLAYBACK_ERROR, msg
210
212
  end
213
+
214
+ # defer any verbose debug logging (i.e. the current state) until after
215
+ # metadata has been successfully loaded because a retryable missing
216
+ # variable may occur a couple hundred times before the condition is
217
+ # satisfied, if ever.
218
+ logger.debug("BEGIN playback state = #{state.inspect}") if logger.debug?
211
219
  logger.debug("Played back response from #{file_path.inspect}.")
212
220
 
213
221
  # determine if epoch is done, which it is if every known request has been
@@ -63,6 +63,23 @@ module RightDevelop::Testing::Client::Rest::Request
63
63
  def to_hash; @headers; end
64
64
  end
65
65
 
66
+ # Overrides transmit to catch halt thrown by log_request.
67
+ #
68
+ # @param [URI[ uri of some kind
69
+ # @param [Net::HTTP] req of some kind
70
+ # @param [RestClient::Payload] of some kind
71
+ #
72
+ # @return
73
+ def transmit(uri, req, payload, &block)
74
+ super
75
+ rescue ::Interrupt
76
+ if @request_timestamp
77
+ logger.warn('Interrupted with at least one request outstanding; will record a timeout response.')
78
+ handle_timeout
79
+ end
80
+ raise
81
+ end
82
+
66
83
  # Overrides log_request for basic logging.
67
84
  #
68
85
  # @param [RestClient::Response] to capture
@@ -88,7 +88,7 @@ module RightDevelop::Testing::Recording
88
88
  class RecordingError < StandardError; end
89
89
  class PlaybackError < StandardError; end
90
90
 
91
- attr_reader :uri, :verb, :http_status, :headers, :body
91
+ attr_reader :uri, :verb, :http_status, :headers, :body, :checksum_data
92
92
  attr_reader :mode, :logger, :effective_route_config, :variables
93
93
  attr_reader :typenames_to_values
94
94
 
@@ -636,8 +636,15 @@ module RightDevelop::Testing::Recording
636
636
  result = variable # use macro as substituted value
637
637
  elsif values = @variables[variable]
638
638
  # quick out for same as initial value; don't show array index.
639
+ # special case for 'any_variable_name[*]' which always uses the last
640
+ # given value instead of keeping an array of values.
639
641
  if values.first == real_value
640
642
  result = variable
643
+ elsif variable.end_with?('[*]')
644
+ # special case for wildcard index; update 1-sized array 0th entry with
645
+ # latest value instead of appending a new unique value.
646
+ @variables[variable][0] = real_value
647
+ result = variable
641
648
  else
642
649
  # show zero-based array index beyond the zero index.
643
650
  unless value_index = values.index(real_value)
@@ -667,8 +674,17 @@ module RightDevelop::Testing::Recording
667
674
  end
668
675
  result = variable # use macro as substituted value
669
676
  else
670
- values = @variables[variable]
671
- case value_index = values && values.index(real_value)
677
+ value_index = nil
678
+ if values = @variables[variable]
679
+ # special wildcard index notation means 'always latest value'; the
680
+ # recorded variable name will be the wildcard index name.
681
+ if variable.end_with?('[*]')
682
+ value_index = 0
683
+ else
684
+ value_index = values.index(real_value)
685
+ end
686
+ end
687
+ case value_index
672
688
  when nil
673
689
  message = 'A variable referenced by a response has not yet been ' +
674
690
  'defined by a request while replacing variable = ' +
@@ -702,7 +718,10 @@ module RightDevelop::Testing::Recording
702
718
  raise PlaybackError, message
703
719
  end
704
720
  elsif variable_array = @variables[variable]
705
- if matched = VARIABLE_INDEX_REGEX.match(target_value)
721
+ if variable.end_with?('[*]')
722
+ # special case for wildcard
723
+ variable_array_index = 0
724
+ elsif matched = VARIABLE_INDEX_REGEX.match(target_value)
706
725
  variable_array_index = Integer(matched[1])
707
726
  else
708
727
  variable_array_index = 0
@@ -791,12 +810,12 @@ module RightDevelop::Testing::Recording
791
810
  end
792
811
 
793
812
  # use deep-sorted JSON to prevent random ordering changing the checksum.
794
- checksum_data = self.class.deep_sorted_json(significant_data)
813
+ @checksum_data = self.class.deep_sorted_json(significant_data)
795
814
  if logger.debug?
796
815
  logger.debug("#{@kind} significant = #{significant.inspect}")
797
- logger.debug("#{@kind} checksum_data = #{checksum_data.inspect}")
816
+ logger.debug("#{@kind} checksum_data = #{@checksum_data.inspect}")
798
817
  end
799
- ::Digest::MD5.hexdigest(checksum_data)
818
+ ::Digest::MD5.hexdigest(@checksum_data)
800
819
  end
801
820
 
802
821
  # Performs a selective copy of any significant fields (recursively) or else
@@ -23,6 +23,7 @@
23
23
  require ::File.expand_path('../../config/init', __FILE__)
24
24
 
25
25
  require 'rack/chunked'
26
+ require 'set'
26
27
  require 'stringio'
27
28
  require 'uri'
28
29
 
@@ -30,6 +31,8 @@ module RightDevelop::Testing::Server::MightApi
30
31
  module App
31
32
  class Base
32
33
 
34
+ MUTEX = ::Mutex.new # semaphore for critical sections
35
+
33
36
  MAX_REDIRECTS = 10 # 500 after so many redirects
34
37
 
35
38
  # Rack (and Skeletor) apps and some known AWS apps only accept dash and
@@ -63,6 +66,18 @@ module RightDevelop::Testing::Server::MightApi
63
66
  nil
64
67
  end
65
68
 
69
+ def self.interrupted?
70
+ !!@interrupted
71
+ end
72
+
73
+ def self.interrupted=(value)
74
+ @interrupted = value
75
+ end
76
+
77
+ def self.app_threads
78
+ @app_threads ||= ::Set.new
79
+ end
80
+
66
81
  def call(env)
67
82
  # HACK: chain trap interrupt on first call to app because the trap chain
68
83
  # does not exist, in rack terms, until just before app is run.
@@ -75,7 +90,11 @@ module RightDevelop::Testing::Server::MightApi
75
90
  # cleaned-up on shutdown, etc. admin mode has a workaround whereby it is
76
91
  # able to clean-up any temporary files immediately after reading its
77
92
  # config and whenever the administered configuration changes.
78
- entrapment unless Base.trapped?
93
+ raise ::Interrupt if self.class.interrupted?
94
+ MUTEX.synchronize do
95
+ entrapment unless Base.trapped?
96
+ self.class.app_threads << ::Thread.current
97
+ end
79
98
 
80
99
  env['rack.logger'] ||= logger
81
100
 
@@ -145,6 +164,13 @@ module RightDevelop::Testing::Server::MightApi
145
164
  logger.error(message)
146
165
  logger.debug(trace.join("\n"))
147
166
  internal_server_error(message)
167
+ rescue ::Interrupt
168
+ # setting interrupted=true may or may not be redundant, depending on
169
+ # visibility of interrupted flag to all outstanding app threads.
170
+ # the problem is that we are not allowed to synchronize a mutex inside
171
+ # of a trap context.
172
+ self.class.interrupted = true
173
+ internal_server_error('interrupt')
148
174
  rescue ::Exception => e
149
175
  message = "Unhandled exception: #{e.class} #{e.message}"
150
176
  trace = e.backtrace || []
@@ -156,6 +182,12 @@ module RightDevelop::Testing::Server::MightApi
156
182
  env['rack.errors'].puts(trace.join("\n"))
157
183
  end
158
184
  internal_server_error(message)
185
+ ensure
186
+ unless self.class.interrupted?
187
+ MUTEX.synchronize do
188
+ self.class.app_threads.delete(::Thread.current)
189
+ end
190
+ end
159
191
  end
160
192
 
161
193
  # Handler.
@@ -434,6 +466,22 @@ EOF
434
466
  app.logger.warn('cannot log traps') # no exception raised
435
467
  end
436
468
 
469
+ # interrupt any running app threads to resolve outstanding requests.
470
+ #
471
+ # note that Mutex#synchronize is not allowed inside a trap context.
472
+ #
473
+ # FIX: duplicating the set is slightly unsafe but not sure how else
474
+ # to deal with data protected by critical section in a trap. we also
475
+ # have logic in ensure block to avoid modifying set on interrupt.
476
+ app.class.interrupted = true
477
+ app_threads = app.class.app_threads.dup
478
+ app_threads.each do |app_thread|
479
+ if app_thread.alive?
480
+ app_thread.raise(::Interrupt)
481
+ app_thread.join
482
+ end
483
+ end
484
+
437
485
  # cleanup fixtures, if requested.
438
486
  app.cleanup
439
487
  if previous_trap && previous_trap.respond_to?(:call)
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: right_develop 3.1.9 ruby lib
5
+ # stub: right_develop 3.1.10 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "right_develop"
9
- s.version = "3.1.9"
9
+ s.version = "3.1.10"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Tony Spataro"]
14
- s.date = "2014-12-01"
14
+ s.date = "2014-12-10"
15
15
  s.description = "A toolkit of development tools created by RightScale."
16
16
  s.email = "support@rightscale.com"
17
17
  s.executables = ["right_develop"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: right_develop
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.9
4
+ version: 3.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Spataro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-08 00:00:00.000000000 Z
11
+ date: 2014-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: right_support