stella 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
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