stella 0.7.1 → 0.7.2

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.
data/CHANGES.txt CHANGED
@@ -1,6 +1,22 @@
1
1
  STELLA, CHANGES
2
2
 
3
3
 
4
+ #### 0.7.2 (2009-10-29) ###############################
5
+
6
+ * FIXED: bin/stella exits with 1 if any request fails.
7
+ * CHANGE: sequential and rsequential resourcing is now global
8
+ across all clients.
9
+ * ADDED: Automatic form parsing for html pages
10
+ * ADDED: Container now has params and headers from the request
11
+ * ADDED: Assume HTTP response codes >= 400 are failed requests
12
+ unless a response block is defined for that status in which
13
+ case the block must return fail.
14
+ * ADDED: Support for HTTP Basic authentication
15
+ * ADDED: Templating for resources parameters and headers.
16
+ * ADDED: Built-in variable :HOSTNAME can be overriden by CLI argument.
17
+ * ADDED: Automatic JSON parsing in response blocks (as 'doc')
18
+
19
+
4
20
  #### 0.7.1 (2009-10-21) ###############################
5
21
 
6
22
  NOTE: Complete rewrite. Features include:
data/Rudyfile CHANGED
@@ -3,7 +3,7 @@
3
3
  machines do
4
4
 
5
5
  region :'us-east-1' do
6
- ami 'ami-e348af8a' # Alestic Debian 5.0, 32-bit (US)
6
+ ami 'ami-212ccf48' # Stella Debian 5.0, 32-bit (US)
7
7
  end
8
8
  region :'eu-west-1' do
9
9
  ami 'ami-6ecde51a' # Alestic Debian 5.0, 32-bit (EU)
@@ -20,8 +20,14 @@ machines do
20
20
  role :gen do
21
21
  user :root
22
22
  size 'm1.large'
23
- ami 'ami-f0f61599'
23
+ ami 'ami-7133d018'
24
24
  end
25
+
26
+ role :demo do
27
+ user :root
28
+ size 'm1.small'
29
+ end
30
+
25
31
  end
26
32
 
27
33
 
@@ -40,9 +46,11 @@ commands do
40
46
  allow :stella
41
47
  allow :rm
42
48
  allow :ulimit
43
-
49
+ allow :ruby19, "/usr/local/bin/ruby"
50
+ allow :gem19_install, "/usr/local/bin/gem", "install"
44
51
  allow :rackup_path do
45
- "/usr/lib/ruby/gems/1.8/gems/stella-#{Stella::VERSION}/support/sample_webapp/config.ru"
52
+ v = [Stella::VERSION::MAJOR, Stella::VERSION::MINOR, Stella::VERSION::TINY].join('.')
53
+ "/usr/lib/ruby/gems/1.8/gems/stella-#{v}/support/sample_webapp/config.ru"
46
54
  end
47
55
  end
48
56
 
@@ -53,9 +61,9 @@ routines do
53
61
  # rudy -r app -v start
54
62
  start do
55
63
  remote do
56
- ulimit :n, '30000'
57
- ulimit :n
58
- rm 'thin.log'
64
+ #ulimit :n, '30000'
65
+ #ulimit :n
66
+ rm :f, 'thin.log'
59
67
  mkdir :p, 'stats'
60
68
  thin :d, :l, 'thin.log', :p, 3114, :R, rackup_path, '--stats', './stats', '--max-conns', 8192, 'start'
61
69
  end
@@ -69,28 +77,33 @@ routines do
69
77
  ps 'ux'
70
78
  end
71
79
  end
72
-
73
- # rudy -v -r gen verify ip-10-251-27-245.ec2.internal:3114
74
- verify do
75
- remote do |arg|
76
- stella :v, 'verify', "#{arg.first}"
77
- end
80
+
81
+ end
82
+
83
+
84
+ # rudy -v -r gen verify ip-10-251-27-245.ec2.internal:3114
85
+ verify do
86
+ remote do |arg|
87
+ file_upload 'examples/essentials/plan.rb'
88
+ file_upload 'examples/essentials/search_terms.txt'
89
+ file_upload 'examples/essentials/logo.png'
90
+ stella :v, 'verify', :p, 'plan.rb', "#{arg.first}"
78
91
  end
79
-
80
- # rudy -v -r gen generate ip-10-251-27-245.ec2.internal:3114
81
- generate do
82
- remote do |arg|
83
- ulimit :n, '30000'
84
- ulimit :n
85
- stella :v, 'generate', :c, 200, :d, '10m', :W, "#{arg.first}"
86
- end
92
+ end
93
+
94
+ # rudy -v -r gen generate ip-10-251-27-245.ec2.internal:3114
95
+ generate do
96
+ remote do |arg|
97
+ file_upload 'examples/essentials/plan.rb'
98
+ file_upload 'examples/essentials/search_terms.txt'
99
+ file_upload 'examples/essentials/logo.png'
100
+ stella :v, 'generate', :p, 'plan.rb', :c, 1, :d, '1m', :W, "#{arg.first}"
87
101
  end
88
-
89
102
  end
90
-
103
+
104
+
91
105
  setup do
92
- before :startup
93
- after :sysupdate, :installdeps, :install_gem
106
+ after :sysupdate, :installdeps, :install_ruby19
94
107
  end
95
108
 
96
109
  shutdown do
@@ -105,6 +118,7 @@ routines do
105
118
 
106
119
  install_rubyforge do
107
120
  remote :root do
121
+ gem19_install 'stella', :V
108
122
  gem_install 'stella', :V
109
123
  end
110
124
  end
@@ -139,6 +153,17 @@ routines do
139
153
 
140
154
  end
141
155
 
156
+ install_zlib do
157
+ remote do
158
+ wget "http://www.zlib.net/zlib-1.2.3.tar.gz"
159
+ tar :x, :z, :f, "zlib-1.2.3.tar.gz"
160
+ cd "zlib-1.2.3"
161
+ configure '--prefix=/usr/local'
162
+ make
163
+ make "install"
164
+ end
165
+ end
166
+
142
167
  installdeps do
143
168
  remote :root do
144
169
  gem_install "test-spec", "rspec", "camping", "fcgi", "memcache-client"
@@ -148,12 +173,23 @@ routines do
148
173
  end
149
174
  end
150
175
 
176
+ install_jruby do
177
+ remote do
178
+ wget 'http://jruby.kenai.com/downloads/1.4.0RC2/jruby-bin-1.4.0RC2.tar.gz'
179
+ tar :x, :z, :f, 'jruby-bin-1.4.0RC2.tar.gz'
180
+ mv 'jruby-1.4.0RC2', '/usr/jruby'
181
+ end
182
+ end
183
+
151
184
  install_ruby19 do
185
+ before :install_zlib
152
186
  remote do
153
- wget 'ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-p243.tar.bz2'
187
+ apt_get "install", "libssl-dev", "libreadline5-dev", "zlib1g-dev"
188
+ #wget 'ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-p243.tar.bz2'
189
+ rm :r, :f, 'ruby-1.9.1-p243'
154
190
  tar :x, :j, :v, :f, 'ruby-1.9.1-p243.tar.bz2'
155
191
  cd 'ruby-1.9.1-p243'
156
- configure '--prefix=/usr/local', '--enable-shared'
192
+ configure '--prefix=/usr/local'
157
193
  make
158
194
  make 'install'
159
195
  end
data/bin/stella CHANGED
@@ -100,6 +100,7 @@ class Stella::CLI::Definition
100
100
  option :W, :nowait, "Ignore wait times"
101
101
  option :w, :wait, Float, "Wait time (in seconds) between client requests (ignored if testplan supplied)"
102
102
  option :p, :testplan, String, "Path to testplan"
103
+ option :'disable-templates', "Disable template parsing"
103
104
  command :generate => Stella::CLI
104
105
 
105
106
  about "Initialize Stella configuration"
@@ -214,7 +214,7 @@ usecase 25, "YAML API" do
214
214
  # vuser will have its own copy of the Array and
215
215
  # iterate through it independently.
216
216
  #
217
- param :lid => rsequential(:listing_ids)
217
+ param :lid => sequential(:listing_ids)
218
218
 
219
219
  # We can use response blocks to affect behaviour
220
220
  # the user. Here we specify that every virtual
@@ -11,7 +11,7 @@ usecase "Exception Handling" do
11
11
  param :what => 'No Such Listing'
12
12
  param :where => random(['Toronto', 'Montreal', 'Vancouver'])
13
13
  response 404 do
14
- raise NoSearchResults
14
+ fail "No results"
15
15
  end
16
16
  end
17
17
 
data/lib/stella.rb CHANGED
@@ -12,6 +12,7 @@ autoload :OpenStruct, 'ostruct'
12
12
  autoload :Storable, 'storable'
13
13
  autoload :Gibbler, 'gibbler/aliases'
14
14
  autoload :Attic, 'attic'
15
+ autoload :ERB, 'erb'
15
16
 
16
17
  require 'benelux'
17
18
 
@@ -52,7 +53,10 @@ module Stella
52
53
  def le(*msg); @logger.puts " " << msg.join("#{$/} ").color(:red); end
53
54
  # Puts +msg+ to +@logger+ if +Rudy.debug?+ returns true
54
55
  def ld(*msg)
55
- @logger.puts "D: " << msg.join("#{$/}D: ") if debug?
56
+ if debug?
57
+ @logger.puts "D(#{Thread.current.object_id}): " << msg.join("#{$/}D: ")
58
+ Stella.lflush
59
+ end
56
60
  end
57
61
 
58
62
  def sysinfo
data/lib/stella/cli.rb CHANGED
@@ -26,7 +26,7 @@ class Stella::CLI < Drydock::Command
26
26
  def generate
27
27
  opts = {}
28
28
  opts[:hosts] = @hosts
29
- [:nowait, :clients, :repetitions, :duration].each do |opt|
29
+ [:nowait, :clients, :repetitions, :duration, :'disable-templates'].each do |opt|
30
30
  opts[opt] = @option.send(opt) unless @option.send(opt).nil?
31
31
  end
32
32
 
data/lib/stella/client.rb CHANGED
@@ -15,9 +15,12 @@ module Stella
15
15
  attr_accessor :base_uri
16
16
  attr_accessor :proxy
17
17
 
18
- def initialize(base_uri=nil, client_id=1)
18
+ def initialize(base_uri=nil, client_id=1, opts={})
19
+ opts = {
20
+ :parse_templates => true
21
+ }.merge! opts
22
+ @opts = opts
19
23
  @base_uri, @client_id = base_uri, client_id
20
-
21
24
  #@cookie_file = File.new("cookies-#{client_id}", 'w')
22
25
  @proxy = OpenStruct.new
23
26
  end
@@ -27,7 +30,7 @@ module Stella
27
30
 
28
31
  http_client = create_http_client
29
32
  stats = {}
30
- container = Container.new(usecase)
33
+ container = Container.new(self.digest_cache, usecase)
31
34
  counter = 0
32
35
  usecase.requests.each do |req|
33
36
  counter += 1
@@ -36,11 +39,27 @@ module Stella
36
39
 
37
40
  stats ||= Benelux::Stats.new
38
41
  update(:prepare_request, usecase, req, counter)
39
- uri_obj = URI.parse(req.uri)
42
+
43
+ # This is for the values that were "set"
44
+ # in the part before the response body.
45
+ prepare_resources(container, req.resources)
46
+
40
47
  params = prepare_params(container, req.params)
41
48
  headers = prepare_headers(container, req.headers)
42
- uri = build_request_uri uri_obj, params, container
43
- raise NoHostDefined, uri_obj if uri.host.nil? || uri.host.empty?
49
+
50
+ container.params, container.headers = params, headers
51
+
52
+ uri = build_request_uri req.uri, params, container
53
+
54
+ if usecase.http_auth
55
+ # TODO: The first arg is domain and can include a URI path.
56
+ # Are there cases where this is important?
57
+ domain = '%s://%s%s' % [uri.scheme, uri.host, '/'] #File.dirname(uri.path)
58
+ user, pass = usecase.http_auth.user, usecase.http_auth.pass
59
+ Stella.li2 " AUTH (#{usecase.http_auth.kind}) #{domain} (#{user}/#{pass})"
60
+ http_client.set_auth(domain, user, pass)
61
+ end
62
+ raise NoHostDefined, req.uri if uri.host.nil? || uri.host.empty?
44
63
  stella_id = [Time.now, self.digest_cache, req.digest_cache, params, headers, counter].gibbler
45
64
 
46
65
  Benelux.add_thread_tags :request => req.digest_cache
@@ -50,39 +69,60 @@ module Stella
50
69
  params['__stella'] = headers['X-Stella-ID']= stella_id[0..10]
51
70
 
52
71
  meth = req.http_method.to_s.downcase
53
- Stella.ld "#{req.http_method}: " << "#{uri_obj.to_s} " << params.inspect
54
-
72
+ Stella.ld "#{req.http_method}: " << "#{req.uri} " << params.inspect
73
+
74
+ ret, asset_duration = nil, 0
55
75
  begin
56
76
  send_request http_client, usecase, meth, uri, req, params, headers, container, counter
57
-
58
77
  Benelux.add_thread_tags :status => container.status
59
- Benelux.thread_timeline.add_count :request_header_size, container.response.request.header.dump.size
60
- Benelux.thread_timeline.add_count :request_content_size, container.response.request.body.content.size
61
- Benelux.thread_timeline.add_count :response_headers_size, container.response.header.dump.size
62
- Benelux.thread_timeline.add_count :response_content_size, container.response.body.content.size
78
+ res = container.response
79
+ [
80
+ [:request_header_size, res.request.header.dump.size],
81
+ [:request_content_size, res.request.body.content.size],
82
+ [:response_headers_size, res.header.dump.size],
83
+ [:response_content_size, res.body.content.size]
84
+ ].each do |att|
85
+ Benelux.thread_timeline.add_count att[0], att[1]
86
+ end
63
87
  ret = execute_response_handler container, req
64
- Benelux.remove_thread_tags :status
65
-
88
+
89
+ asset_start = Time.now
90
+ container.assets.each do |uri|
91
+ Benelux.add_thread_tags :asset => uri
92
+ a = http_client.get uri
93
+ Stella.li3 " FETCH ASSET: #{uri} #{a.status}"
94
+ Benelux.remove_thread_tags :asset
95
+ end
96
+ asset_duration = Time.now - asset_start
97
+
66
98
  rescue => ex
67
99
  Benelux.thread_timeline.add_count :failed, 1
68
100
  update(:request_error, usecase, uri, req, params, ex)
101
+ Benelux.remove_thread_tags :status, :retry, :request, :stella_id
69
102
  next
70
103
  end
71
104
 
72
105
  Stella.lflush
73
106
 
74
- run_sleeper(req.wait) if req.wait && !nowait?
107
+ run_sleeper(req.wait, asset_duration) if req.wait != 0 && !nowait?
75
108
 
76
109
  # TODO: consider throw/catch
77
110
  case ret.class.to_s
78
111
  when "Stella::Client::Repeat"
112
+ Benelux.remove_thread_tags :status
79
113
  update(:repeat_request, counter, ret.times+1)
80
114
  redo if counter <= ret.times
81
115
  when "Stella::Client::Quit"
116
+ Benelux.remove_thread_tags :status
82
117
  update(:quit_usecase, ret.message)
83
118
  break
119
+ when "Stella::Client::Fail"
120
+ Benelux.thread_timeline.add_count :failed, 1
121
+ update(:fail_request, ret.message)
84
122
  end
85
-
123
+
124
+ Benelux.remove_thread_tags :status
125
+
86
126
  counter = 0 # reset
87
127
  end
88
128
  Benelux.remove_thread_tags :retry, :request, :stella_id
@@ -103,14 +143,18 @@ module Stella
103
143
  changed and notify_observers(kind, self.digest_cache, *args)
104
144
  end
105
145
 
106
- def run_sleeper(wait)
146
+ def run_sleeper(wait, already_waited=0)
147
+ # The time it took to download the assets can
148
+ # be removed from the specified wait time.
107
149
  if wait.is_a?(::Range)
108
150
  ms = rand(wait.last * 1000).to_f
109
151
  ms = wait.first if ms < wait.first
110
152
  else
111
153
  ms = wait * 1000
112
154
  end
113
- sleep ms / 1000
155
+ sec = ms / 1000
156
+ Stella.ld "WAIT ADJUSTED FROM %.1f TO: %.1f" % [sec, (sec - already_waited)]
157
+ sleep (sec - already_waited) if (sec - already_waited) > 0
114
158
  end
115
159
 
116
160
  def create_http_client
@@ -123,24 +167,32 @@ module Stella
123
167
  http_client.set_proxy_auth(@proxy.user, @proxy.pass) if @proxy.user
124
168
  http_client.debug_dev = STDOUT if Stella.debug? && Stella.loglev > 3
125
169
  http_client.protocol_version = "HTTP/1.1"
170
+ http_client.ssl_config.verify_mode = ::OpenSSL::SSL::VERIFY_NONE
126
171
  http_client
127
172
  end
128
173
 
129
- def prepare_params(container, params)
130
- newparams = {}
131
- params.each_pair do |n,v|
132
- Stella.ld "PREPARE PARAM: #{n}"
133
- v = container.instance_eval &v if v.is_a?(Proc)
134
- newparams[n] = v
135
- end
136
- newparams
174
+ def prepare_resources(container, resources)
175
+ h = prepare_runtime_hash container, resources
176
+ # p [container.client_id.shorter, h]
177
+ container.resources.merge! h
137
178
  end
138
179
 
139
- def prepare_headers(container, headers)
140
- Stella.ld "PREPARE HEADERS: #{headers}"
141
- headers = container.instance_eval &headers if headers.is_a?(Proc)
142
- headers
180
+ # Process resource values from the request object
181
+ def prepare_runtime_hash(container, hashobj, &extra)
182
+ newh = {}
183
+ #Stella.ld "PREPARE HEADERS: #{headers}"
184
+ hashobj.each_pair do |n,v|
185
+ v = container.instance_eval &v if v.is_a?(Proc)
186
+ if @opts[:parse_templates]
187
+ v = container.parse_template v if String === v
188
+ end
189
+ v = extra.call(v) unless extra.nil?
190
+ newh[n] = v
191
+ end
192
+ newh
143
193
  end
194
+ alias_method :prepare_headers, :prepare_runtime_hash
195
+ alias_method :prepare_params, :prepare_runtime_hash
144
196
 
145
197
  # Testplan URIs can be relative or absolute. Either one can
146
198
  # contain variables in the form <tt>:varname</tt>, as in:
@@ -151,7 +203,17 @@ module Stella
151
203
  # if necessary and replaces all variables with literal values.
152
204
  # If no replacement value can be found, the variable will remain.
153
205
  def build_request_uri(uri, params, container)
154
- uri = URI::HTTP.build({:path => uri}) unless uri.is_a?(URI::Generic)
206
+ newuri = uri.clone # don't modify uri template
207
+ # We call uri.clone b/c we modify uri.
208
+ uri.scan(/:([a-z_]+)/i) do |instances|
209
+ instances.each do |varname|
210
+ val = find_replacement_value(varname, params, container, base_uri)
211
+ #Stella.ld "FOUND: #{val}"
212
+ newuri.gsub! /:#{varname}/, val.to_s unless val.nil?
213
+ end
214
+ end
215
+
216
+ uri = URI.parse(newuri)
155
217
 
156
218
  if uri.host.nil? && base_uri.nil?
157
219
  Stella.abort!
@@ -163,15 +225,7 @@ module Stella
163
225
  uri.port = base_uri.port if uri.port.nil?
164
226
  uri.path ||= ''
165
227
  uri.path.gsub! /\/$/, '' # Don't double up on the first slash
166
- # We call req.uri again because we need
167
- # to modify request_uri inside the loop.
168
- uri.path.clone.scan(/:([a-z_]+)/i) do |instances|
169
- instances.each do |varname|
170
- val = find_replacement_value(varname, params, container)
171
- #Stella.ld "FOUND: #{val}"
172
- uri.path.gsub! /:#{varname}/, val.to_s unless val.nil?
173
- end
174
- end
228
+
175
229
  uri
176
230
  end
177
231
 
@@ -179,35 +233,47 @@ module Stella
179
233
  # This method looks at the request parameters and then at the
180
234
  # usecase's resource hash for a replacement value.
181
235
  # If not found, returns nil.
182
- def find_replacement_value(name, params, container)
236
+ def find_replacement_value(name, params, container, base_uri)
183
237
  value = nil
184
238
  #Stella.ld "REPLACE: #{name}"
185
239
  #Stella.ld "PARAMS: #{params.inspect}"
186
240
  #Stella.ld "IVARS: #{container.instance_variables}"
187
- value = params[name.to_sym]
241
+ if name.to_sym == :HOSTNAME && !base_uri.nil?
242
+ value = base_uri.host
243
+ elsif params.has_key?(name.to_sym)
244
+ value = params.delete name.to_sym
245
+ end
188
246
  value = container.resource name.to_sym if value.nil?
189
247
  value
190
248
  end
191
249
 
192
250
  # Find the appropriate response handler by executing the
193
251
  # HTTP response status against the configured handlers.
194
- # If several match, the first one is used.
195
- def execute_response_handler(container, req)
252
+ # If several match, the first one is returned.
253
+ def find_response_handler(container, req)
196
254
  handler = nil
197
255
  req.response.each_pair do |regex,h|
198
256
  Stella.ld "HANDLER REGEX: #{regex.to_s} (#{container.status})"
199
257
  regex = /#{regex}/ unless regex.is_a? Regexp
200
258
  handler = h and break if container.status.to_s =~ regex
201
259
  end
260
+ handler
261
+ end
262
+
263
+
264
+ def execute_response_handler(container, req)
202
265
  ret = nil
203
- unless handler.nil?
204
- begin
205
- ret = container.instance_eval &handler
206
- update(:execute_response_handler, req, container)
207
- rescue => ex
208
- update(:error_execute_response_handler, ex, req, container)
209
- Stella.ld ex.message, ex.backtrace
210
- end
266
+ handler = find_response_handler container, req
267
+ if handler.nil?
268
+ Benelux.thread_timeline.add_count :failed, 1 if container.status >= 400
269
+ return
270
+ end
271
+ begin
272
+ ret = container.instance_eval &handler
273
+ update(:execute_response_handler, req, container)
274
+ rescue => ex
275
+ update(:error_execute_response_handler, ex, req, container)
276
+ Stella.ld ex.message, ex.backtrace
211
277
  end
212
278
  ret
213
279
  end