blitz 0.1.0

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,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ gem "rest-client", "~> 1.6.1"
7
+ gem "json", "~> 1.4.6"
8
+ gem "json_pure", "~> 1.4.6"
9
+ gem "hexy", "~> 0.1.1"
10
+
11
+ # Add dependencies to develop your gem here.
12
+ # Include everything needed to run rake, tests, features, etc.
13
+ group :development do
14
+ gem "bundler", "~> 1.0.0"
15
+ gem "jeweler", "~> 1.5.1"
16
+ end
@@ -0,0 +1,26 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ hexy (0.1.1)
6
+ jeweler (1.5.1)
7
+ bundler (~> 1.0.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ json (1.4.6)
11
+ json_pure (1.4.6)
12
+ mime-types (1.16)
13
+ rake (0.8.7)
14
+ rest-client (1.6.1)
15
+ mime-types (>= 1.16)
16
+
17
+ PLATFORMS
18
+ ruby
19
+
20
+ DEPENDENCIES
21
+ bundler (~> 1.0.0)
22
+ hexy (~> 0.1.1)
23
+ jeweler (~> 1.5.1)
24
+ json (~> 1.4.6)
25
+ json_pure (~> 1.4.6)
26
+ rest-client (~> 1.6.1)
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Mu Dynamics
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,22 @@
1
+ ## ![blitz.io](http://blitz.io/images/logo2.png)
2
+
3
+ Make load and performance a fun sport.
4
+
5
+ * Run a sprint from around the world
6
+ * Rush your API and website to scale it out
7
+ * Condition your site around the clock
8
+
9
+ ## Getting started
10
+ Login to [blitz.io](http://blitz.io) and in the blitz bar type:
11
+ --api-key
12
+
13
+ Now
14
+ gem install blitz
15
+
16
+ and run a sprint like this:
17
+ blitz curl --region california http://blitz.io
18
+
19
+ and you can rush like this:
20
+ blitz curl --region california --pattern 1-100:60 http://blitz.io
21
+
22
+ Copyright (c) 2011 Mu Dynamics. See LICENSE.txt for further details.
@@ -0,0 +1,51 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+
13
+ require 'rake'
14
+ require 'jeweler'
15
+ require 'blitz'
16
+ Jeweler::Tasks.new do |gem|
17
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
18
+ gem.name = "blitz"
19
+ gem.homepage = "http://blitz.io"
20
+ gem.license = "MIT"
21
+ gem.summary = %Q{Make load and performance testing a fun sport}
22
+ gem.description = %Q{Make load and performance testing a fun sport}
23
+ gem.email = "kowsik@gmail.com"
24
+ gem.authors = ["pcapr"]
25
+ gem.version = Blitz::Version
26
+ gem.executables = ['blitz']
27
+
28
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
29
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
30
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
31
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
32
+ end
33
+ Jeweler::RubygemsDotOrgTasks.new
34
+
35
+ require 'rake/testtask'
36
+ Rake::TestTask.new(:test) do |test|
37
+ test.libs << 'lib' << 'test'
38
+ test.pattern = 'test/**/test_*.rb'
39
+ test.verbose = true
40
+ end
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ rdoc.rdoc_dir = 'rdoc'
47
+ rdoc.title = "blitz #{Blitz::Version}"
48
+ rdoc.rdoc_files.include('README*')
49
+ rdoc.rdoc_files.include('lib/**/*.rb')
50
+ end
51
+
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'blitz'
7
+
8
+ argv = ARGV.dup
9
+ ARGV.clear
10
+ cmd = argv.shift.strip rescue 'help'
11
+ Blitz.run cmd, argv
@@ -0,0 +1,80 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{blitz}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["pcapr"]
12
+ s.date = %q{2011-03-10}
13
+ s.default_executable = %q{blitz}
14
+ s.description = %q{Make load and performance testing a fun sport}
15
+ s.email = %q{kowsik@gmail.com}
16
+ s.executables = ["blitz"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE.txt",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE.txt",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "bin/blitz",
29
+ "blitz.gemspec",
30
+ "lib/blitz.rb",
31
+ "lib/blitz/client.rb",
32
+ "lib/blitz/command.rb",
33
+ "lib/blitz/command/api.rb",
34
+ "lib/blitz/command/curl.rb",
35
+ "lib/blitz/command/help.rb",
36
+ "lib/blitz/curl/error.rb",
37
+ "lib/blitz/curl/sprint.rb",
38
+ "lib/blitz/helper.rb",
39
+ "test/helper.rb",
40
+ "test/test_blitz.rb"
41
+ ]
42
+ s.homepage = %q{http://blitz.io}
43
+ s.licenses = ["MIT"]
44
+ s.require_paths = ["lib"]
45
+ s.rubygems_version = %q{1.3.7}
46
+ s.summary = %q{Make load and performance testing a fun sport}
47
+ s.test_files = [
48
+ "test/helper.rb",
49
+ "test/test_blitz.rb"
50
+ ]
51
+
52
+ if s.respond_to? :specification_version then
53
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
54
+ s.specification_version = 3
55
+
56
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
+ s.add_runtime_dependency(%q<rest-client>, ["~> 1.6.1"])
58
+ s.add_runtime_dependency(%q<json>, ["~> 1.4.6"])
59
+ s.add_runtime_dependency(%q<json_pure>, ["~> 1.4.6"])
60
+ s.add_runtime_dependency(%q<hexy>, ["~> 0.1.1"])
61
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
62
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
63
+ else
64
+ s.add_dependency(%q<rest-client>, ["~> 1.6.1"])
65
+ s.add_dependency(%q<json>, ["~> 1.4.6"])
66
+ s.add_dependency(%q<json_pure>, ["~> 1.4.6"])
67
+ s.add_dependency(%q<hexy>, ["~> 0.1.1"])
68
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
69
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
70
+ end
71
+ else
72
+ s.add_dependency(%q<rest-client>, ["~> 1.6.1"])
73
+ s.add_dependency(%q<json>, ["~> 1.4.6"])
74
+ s.add_dependency(%q<json_pure>, ["~> 1.4.6"])
75
+ s.add_dependency(%q<hexy>, ["~> 0.1.1"])
76
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
77
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
78
+ end
79
+ end
80
+
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'hexy'
3
+
4
+ class Blitz
5
+ require 'blitz/helper'
6
+ Version = "0.1.0".freeze
7
+
8
+ extend Blitz::Helper
9
+
10
+ def self.run cmd, argv
11
+ kname, mname = cmd.split(':', 2)
12
+ klass = Blitz::Command.const_get kname.capitalize rescue nil
13
+ mname ||= 'default'
14
+ mname = "cmd_#{mname}".to_sym
15
+ if klass and klass < Blitz::Command and klass.method_defined? mname
16
+ command = klass.new
17
+ begin
18
+ command.send mname, argv
19
+ rescue Test::Unit::AssertionFailedError, ArgumentError => e
20
+ error e.message.chomp('.')
21
+ end
22
+ else
23
+ error "Unknown command #{cmd}"
24
+ end
25
+ end
26
+ end
27
+
28
+ require 'blitz/client'
29
+ require 'blitz/curl/error'
30
+ require 'blitz/curl/sprint'
31
+ require 'blitz/command'
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'json/pure'
3
+ require 'restclient'
4
+
5
+ class Blitz
6
+ class Client
7
+ attr_reader :blitz
8
+
9
+ def initialize user, apik, host='blitz.io'
10
+ scheme = host.index('localhost') ? 'http' : 'https'
11
+ @blitz = RestClient::Resource.new "#{scheme}://#{host}", \
12
+ :headers => {
13
+ :x_api_user => user,
14
+ :x_api_key => apik,
15
+ :x_gem_version => ::Blitz::Version
16
+ }
17
+ end
18
+
19
+ def curl_execute data
20
+ JSON.parse blitz['/api/1/curl/execute'].post(data.to_json)
21
+ end
22
+
23
+ def login
24
+ JSON.parse blitz['/login/api'].get
25
+ end
26
+
27
+ def job_status job_id
28
+ JSON.parse blitz["/api/1/jobs/#{job_id}/status"].get
29
+ end
30
+
31
+ def abort_job job_id
32
+ JSON.parse blitz["/api/1/jobs/#{job_id}/abort"].put ''
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ require 'test/unit/assertions'
2
+
3
+ # The default template string contains what was sent and received. Strip
4
+ # these out since we don't need them
5
+ class Test::Unit::Assertions::AssertionMessage
6
+ alias :old_template :template
7
+
8
+ def template
9
+ @template_string = ''
10
+ @parameters = []
11
+ old_template
12
+ end
13
+ end
14
+
15
+ class Blitz
16
+ class Command
17
+ include Test::Unit::Assertions
18
+ include Helper
19
+ end
20
+ end # Blitz
21
+
22
+ Dir["#{File.dirname(__FILE__)}/command/*.rb"].each { |c| require c }
@@ -0,0 +1,96 @@
1
+ class Blitz
2
+ class Command
3
+ class API < Command
4
+ attr_accessor :credentials
5
+
6
+ def cmd_init argv
7
+ FileUtils.rm credentials_file rescue nil
8
+ API.client
9
+
10
+ msg "You are now ready to blitz!"
11
+ msg "Try blitz help to learn more about the commands."
12
+ end
13
+
14
+ def client
15
+ get_credentials
16
+ Blitz::Client.new(user, password, host)
17
+ end
18
+
19
+ def user
20
+ get_credentials
21
+ @credentials[0]
22
+ end
23
+
24
+ def password
25
+ get_credentials
26
+ @credentials[1]
27
+ end
28
+
29
+ def host
30
+ ENV['BLITZ_HOST'] || 'blitz.io'
31
+ end
32
+
33
+ def credentials_file
34
+ ENV['HOME'] + '/.blitz/credentials'
35
+ end
36
+
37
+ def get_credentials
38
+ return if @credentials
39
+ unless @credentials = read_credentials
40
+ @credentials = ask_for_credentials
41
+ save_credentials
42
+ end
43
+ @credentials
44
+ end
45
+
46
+ def read_credentials
47
+ File.exists?(credentials_file) and File.read(credentials_file).split("\n")
48
+ end
49
+
50
+ def ask_for_credentials
51
+ msg "Enter your blitz credentials."
52
+ print "User-ID: "
53
+ user = ask
54
+ print "API-Key: "
55
+ apik = ask
56
+ apik2 = Blitz::Client.new(user, apik, host).login['api_key']
57
+ if not apik2
58
+ error "Authentication failed"
59
+ exit 1
60
+ end
61
+
62
+ [ user, apik2 ]
63
+ end
64
+
65
+ def save_credentials
66
+ write_credentials
67
+ end
68
+
69
+ def write_credentials
70
+ FileUtils.mkdir_p(File.dirname(credentials_file))
71
+ File.open(credentials_file, 'w') do |f|
72
+ f.puts self.credentials
73
+ end
74
+ set_credentials_permissions
75
+ end
76
+
77
+ def set_credentials_permissions
78
+ FileUtils.chmod 0700, File.dirname(credentials_file)
79
+ FileUtils.chmod 0600, credentials_file
80
+ end
81
+
82
+ def self.instance
83
+ @instance ||= API.new
84
+ end
85
+
86
+ def self.client
87
+ self.instance.client
88
+ end
89
+
90
+ private
91
+ def initialize
92
+ end
93
+ end
94
+ Api = API
95
+ end # Command
96
+ end # Blitz
@@ -0,0 +1,272 @@
1
+ class Blitz
2
+ class Command
3
+ class Curl < Command
4
+ def cmd_help argv
5
+ help
6
+ end
7
+
8
+ def cmd_run argv
9
+ args = parse_cli argv
10
+ if args['help']
11
+ return help
12
+ end
13
+
14
+ if not args['pattern']
15
+ sprint args
16
+ return
17
+ else
18
+ error "Rushing not supported yet, but coming soon"
19
+ end
20
+ end
21
+
22
+ alias_method :cmd_default, :cmd_run
23
+
24
+ private
25
+ def authorize_error e
26
+ base_url = "#{e.scheme}://#{e.host}:#{e.port}"
27
+ puts
28
+ error "You haven't verified that you are the devops dude for #{e.host}"
29
+ error ""
30
+ error "Make sure the following URL is reachable by us and returns the value 42."
31
+ error "#{base_url}/#{e.uuid}"
32
+ error ""
33
+ error "If your app is RESTfully built with sinatra or rails, simply"
34
+ error "add a route like so:"
35
+ error ""
36
+ error "get '/#{e.uuid}' do"
37
+ error " '42'"
38
+ error "end"
39
+ error ""
40
+ error "And we'll be on our merry way to blitz #{e.host}"
41
+ puts
42
+ end
43
+
44
+ def sprint args
45
+ begin
46
+ job = ::Blitz::Curl::Sprint.execute args
47
+ result = job.result
48
+ print_sprint_result args, result
49
+ rescue ::Blitz::Curl::Error::Authorize => e
50
+ authorize_error e
51
+ rescue ::Blitz::Curl::Error::Region => e
52
+ error "#{e.region}: #{e.message}"
53
+ rescue ::Blitz::Curl::Error => e
54
+ error e.message
55
+ end
56
+ end
57
+
58
+ def print_sprint_result args, result
59
+ rtt = result.duration
60
+ if rtt < 1.0
61
+ rtt = (rtt * 1000).floor.to_s + ' ms';
62
+ else
63
+ rtt = ("%.2f" % rtt) + ' sec';
64
+ end
65
+
66
+ puts "-" * 70
67
+ msg "#{result.region}: Response time of #{rtt}"
68
+ unless args['dump-header'] or args['verbose']
69
+ msg "Try --verbose to see the request/response headers"
70
+ end
71
+ puts "-" * 70
72
+ puts
73
+
74
+ if args['dump-header'] or args['verbose']
75
+ puts "> " + result.request.line
76
+ result.request.headers.each_pair { |k, v| puts "> #{k}: #{v}\r\n" }
77
+ puts
78
+
79
+ content = result.request.content
80
+ if not content.empty?
81
+ if /^[[:print:]]+$/ =~ content
82
+ puts content
83
+ else
84
+ puts Hexy.new(content).to_s
85
+ end
86
+ puts
87
+ end
88
+
89
+ puts "< " + result.response.line
90
+ result.response.headers.each_pair { |k, v| puts "> #{k}: #{v}\r\n" }
91
+ puts
92
+ end
93
+
94
+ content = result.response.content
95
+ if not content.empty?
96
+ if /^[[:print:]]+$/ =~ content
97
+ puts content
98
+ else
99
+ puts Hexy.new(content).to_s
100
+ end
101
+ end
102
+ end
103
+
104
+ def help
105
+ helps = [
106
+ { :short => '-A', :long => '--user-agent', :value => '<string>', :help => 'User-Agent to send to server' },
107
+ { :short => '-b', :long => '--cookie', :value => 'name=<string>', :help => 'Cookie to send to the server (multiple)' },
108
+ { :short => '-d', :long => '--data', :value => '<string>', :help => 'Data to send in a PUT or POST request' },
109
+ { :short => '-D', :long => '--dump-header', :value => '<file>', :help => 'Print the request/response headers' },
110
+ { :short => '-e', :long => '--referer', :value => '<string>', :help => 'Referer URL' },
111
+ { :short => '-h', :long => '--help', :value => '', :help => 'Help on command line options' },
112
+ { :short => '-H', :long => '--header', :value => '<string>', :help => 'Custom header to pass to server' },
113
+ { :short => '-p', :long => '--pattern', :value => '<s>-<e>:<d>', :help => 'Ramp from s to e concurrent requests in d secs' },
114
+ { :short => '-r', :long => '--region', :value => '<string>', :help => 'Choose one of california, virginia, singapore or ireland' },
115
+ { :short => '-s', :long => '--status', :value => '<number>', :help => 'Assert on the HTTP response status code' },
116
+ { :short => '-T', :long => '--timeout', :value => '<ms>', :help => 'Wait time for both connect and responses' },
117
+ { :short => '-u', :long => '--user', :value => '<user[:pass]>', :help => 'User and password for authentication' },
118
+ { :short => '-X', :long => '--request', :value => '<string>', :help => 'Request method to use (GET, HEAD, PUT, etc.)' },
119
+ { :short => '-v', :long => '--verbose', :value => '', :help => 'Print the request/response headers' },
120
+ { :short => '-1', :long => '--tlsv1', :value => '', :help => 'Use TLSv1 (SSL)' },
121
+ { :short => '-2', :long => '--sslv2', :value => '', :help => 'Use SSLv2 (SSL)' },
122
+ { :short => '-3', :long => '--sslv3', :value => '', :help => 'Use SSLv3 (SSL)' }
123
+ ]
124
+
125
+ max_long_size = helps.inject(0) { |memo, obj| [ obj[:long].size, memo ].max }
126
+ max_value_size = helps.inject(0) { |memo, obj| [ obj[:value].size, memo ].max }
127
+ puts
128
+ msg "Usage: blitz curl <options> <url>"
129
+ puts
130
+ helps.each do |h|
131
+ msg "%-*s %*s %-*s %s" % [max_long_size, h[:long], 2, h[:short], max_value_size, h[:value], h[:help]]
132
+ end
133
+ puts
134
+ end
135
+
136
+ def parse_cli argv
137
+ hash = Hash.new
138
+ while not argv.empty?
139
+ break if argv.first[0,1] != '-'
140
+
141
+ k = argv.shift
142
+ if [ '-A', '--user-agent' ].member? k
143
+ hash['user-agent'] = shift(k, argv)
144
+ next
145
+ end
146
+
147
+ if [ '-b', '--cookie' ].member? k
148
+ # TODO: support cookie jars
149
+ hash['cookies'] ||= []
150
+ hash['cookies'] << shift(k, argv)
151
+ next
152
+ end
153
+
154
+ if [ '-d', '--data' ].member? k
155
+ hash['content'] ||= Hash.new
156
+ hash['content']['data'] ||= []
157
+ v = shift(k, argv)
158
+ v = File.read v[1..-1] if v =~ /^@/
159
+ hash['content']['data'] << v
160
+ next
161
+ end
162
+
163
+ if [ '-D', '--dump-header' ].member? k
164
+ hash['dump-header'] = shift(k, argv)
165
+ next
166
+ end
167
+
168
+ if [ '-e', '--referer'].member? k
169
+ hash['referer'] = shift(k, argv)
170
+ next
171
+ end
172
+
173
+ if [ '-h', '--help' ].member? k
174
+ hash['help'] = true
175
+ next
176
+ end
177
+
178
+ if [ '-H', '--header' ].member? k
179
+ hash['headers'] ||= []
180
+ hash['headers'].push shift(k, argv)
181
+ next
182
+ end
183
+
184
+ if [ '-p', '--pattern' ].member? k
185
+ v = shift(k, argv)
186
+ if not /^(\d+)-(\d+):(\d+)$/ =~ v
187
+ raise Test::Unit::AssertionFailedError, "invalid ramp pattern"
188
+ end
189
+ hash['pattern'] = {
190
+ 'iterations' => 1,
191
+ 'intervals' => [{
192
+ 'iterations' => 1,
193
+ 'start' => $1.to_i,
194
+ 'end' => $2.to_i,
195
+ 'duration' => $3.to_i
196
+ }]
197
+ }
198
+ next
199
+ end
200
+
201
+ if [ '-r', '--region' ].member? k
202
+ v = shift(k, argv)
203
+ assert_match(/^california|virginia|singapore|ireland|japan$/, v, 'region must be one of california, virginia, singapore, japan or ireland')
204
+ hash['region'] = v
205
+ next
206
+ end
207
+
208
+ if [ '-s', '--status' ].member? k
209
+ hash['status'] = shift(k, argv).to_i
210
+ next
211
+ end
212
+
213
+ if [ '-T', '--timeout' ].member? k
214
+ hash['timeout'] = shift(k, argv).to_i
215
+ next
216
+ end
217
+
218
+ if [ '-u', '--user' ].member? k
219
+ hash['user'] = shift(k, argv)
220
+ next
221
+ end
222
+
223
+ if [ '-X', '--request' ].member? k
224
+ hash['request'] = shift(k, argv)
225
+ next
226
+ end
227
+
228
+ if [ '-v', '--verbose' ].member? k
229
+ hash['verbose'] = true
230
+ next
231
+ end
232
+
233
+ if [ '-1', '--tlsv1' ].member? k
234
+ hash['ssl'] = 'tlsv1'
235
+ next
236
+ end
237
+
238
+ if [ '-2', '--sslv2' ].member? k
239
+ hash['ssl'] = 'sslv2'
240
+ next
241
+ end
242
+
243
+ if [ '-3', '--sslv3' ].member? k
244
+ hash['ssl'] = 'sslv2'
245
+ next
246
+ end
247
+
248
+ raise ArgumentError, "Unknown option #{k}"
249
+ end
250
+
251
+ if not hash['help']
252
+ url = argv.shift
253
+ assert_not_nil(url, 'no URL specified!')
254
+ hash['url'] = url
255
+ end
256
+
257
+ if hash.member? 'content'
258
+ data_size = hash['content']['data'].inject(0) { |m, v| m + v.size }
259
+ assert(data_size < 10*1024, "POST content must be < 10K")
260
+ end
261
+
262
+ hash
263
+ end
264
+
265
+ def shift key, argv
266
+ val = argv.shift
267
+ assert_not_nil(val, "missing value for #{key}")
268
+ val
269
+ end
270
+ end # Curl
271
+ end # Command
272
+ end # Blitz
@@ -0,0 +1,22 @@
1
+ class Blitz
2
+ class Command
3
+ class Help < Command
4
+ def cmd_default argv
5
+ puts
6
+ msg "Usage: blitz <command> <options>"
7
+ helps = [
8
+ { :cmd => 'help', :help => "Display this help" },
9
+ { :cmd => 'api:init', :help => 'Validate your API key' },
10
+ { :cmd => 'curl', :help => 'Run a sprint or a rush' },
11
+ { :cmd => 'curl:help', :help => 'Show help on sprint and rushing' }
12
+ ]
13
+
14
+ max_cmd_size = helps.inject(0) { |memo, obj| [ obj[:cmd].size, memo ].max } + 4
15
+ helps.each do |h|
16
+ msg "%*s - %s" % [max_cmd_size, h[:cmd], h[:help]]
17
+ end
18
+ puts
19
+ end
20
+ end
21
+ end # Command
22
+ end # Blitz
@@ -0,0 +1,45 @@
1
+ class Blitz
2
+ module Curl
3
+ class Error < StandardError
4
+ def initialize json={}
5
+ super json['reason'] || "Hmmm, something went wrong. Try again in a little bit?"
6
+ end
7
+
8
+ class Authorize < Error
9
+ attr_reader :scheme, :host, :port, :uuid
10
+
11
+ def initialize json
12
+ @scheme = json['scheme']
13
+ @host = json['host']
14
+ @port = json['port']
15
+ @uuid = json['uuid']
16
+ super
17
+ end
18
+ end
19
+
20
+ class Region < Error
21
+ attr_reader :region
22
+
23
+ def initialize json
24
+ @region = json['region']
25
+ super
26
+ end
27
+ end
28
+
29
+ class DNS < Region
30
+ end
31
+
32
+ class Connect < Region
33
+ end
34
+
35
+ class Timeout < Region
36
+ end
37
+
38
+ class Parse < Region
39
+ end
40
+
41
+ class Status < Region
42
+ end
43
+ end
44
+ end # Curl
45
+ end # Blitz
@@ -0,0 +1,108 @@
1
+ class Blitz
2
+ module Curl
3
+ class Sprint
4
+ class Request
5
+ attr_reader :line
6
+ attr_reader :method
7
+ attr_reader :url
8
+ attr_reader :headers
9
+ attr_reader :content
10
+
11
+ def initialize json
12
+ @line = json['line']
13
+ @method = json['method']
14
+ @url = json['url']
15
+ @content = json['content'].unpack('m')[0]
16
+ @headers = json['headers']
17
+ end
18
+ end
19
+
20
+ class Response
21
+ attr_reader :line
22
+ attr_reader :status
23
+ attr_reader :message
24
+ attr_reader :headers
25
+ attr_reader :content
26
+
27
+ def initialize json
28
+ @line = json['line']
29
+ @status = json['status']
30
+ @message = json['message']
31
+ @content = json['content'].unpack('m')[0]
32
+ @headers = json['headers']
33
+ end
34
+ end
35
+
36
+ class Result
37
+ attr_reader :region
38
+ attr_reader :duration
39
+ attr_reader :connect
40
+ attr_reader :request
41
+ attr_reader :response
42
+
43
+ def initialize json
44
+ result = json['result']
45
+ @region = result['region']
46
+ @duration = result['duration']
47
+ @connect = result['connect']
48
+ @request = Request.new result['request']
49
+ @response = Response.new result['response']
50
+ end
51
+ end
52
+
53
+ def self.execute args
54
+ args.delete 'pattern'
55
+
56
+ res = Command::API.client.curl_execute args
57
+ raise Error.new(res) if res['error']
58
+ return self.new res['job_id']
59
+ end
60
+
61
+ attr_reader :job_id
62
+
63
+ def initialize job_id
64
+ @job_id = job_id
65
+ end
66
+
67
+ def result
68
+ while true
69
+ sleep 2.0
70
+
71
+ job = Command::API.client.job_status job_id
72
+ if job['error']
73
+ raise Error
74
+ end
75
+
76
+ result = job['result']
77
+ next if job['status'] == 'queued'
78
+ next if job['status'] == 'running' and not result
79
+
80
+ raise Error if not result
81
+
82
+ error = result['error']
83
+ if error
84
+ if error == 'dns'
85
+ raise Error::DNS.new(result)
86
+ elsif error == 'connect'
87
+ raise Error::Connect.new(result)
88
+ elsif error == 'timeout'
89
+ raise Error::Timeout.new(result)
90
+ elsif error == 'parse'
91
+ raise Error::Parse.new(result)
92
+ elsif result['assert'] == 0
93
+ raise Error::Status.new(result)
94
+ else
95
+ raise Error
96
+ end
97
+ end
98
+
99
+ return Result.new(job)
100
+ end
101
+ end
102
+
103
+ def abort
104
+ Command::API.client.abort_job job_id rescue nil
105
+ end
106
+ end
107
+ end # Curl
108
+ end # Blitz
@@ -0,0 +1,15 @@
1
+ class Blitz
2
+ module Helper
3
+ def error msg
4
+ $stderr.puts "!! #{msg}"
5
+ end
6
+
7
+ def msg msg
8
+ puts msg
9
+ end
10
+
11
+ def ask
12
+ gets.strip
13
+ end
14
+ end
15
+ end # Blitz
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+
12
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ require 'blitz'
15
+
16
+ class Test::Unit::TestCase
17
+ end
@@ -0,0 +1,6 @@
1
+ require 'helper'
2
+
3
+ class TestBlitz < Test::Unit::TestCase
4
+ def test_something_for_real
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,182 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: blitz
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - pcapr
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-10 00:00:00 -08:00
19
+ default_executable: blitz
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ type: :runtime
23
+ prerelease: false
24
+ name: rest-client
25
+ version_requirements: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ hash: 13
31
+ segments:
32
+ - 1
33
+ - 6
34
+ - 1
35
+ version: 1.6.1
36
+ requirement: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ type: :runtime
39
+ prerelease: false
40
+ name: json
41
+ version_requirements: &id002 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ hash: 11
47
+ segments:
48
+ - 1
49
+ - 4
50
+ - 6
51
+ version: 1.4.6
52
+ requirement: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ type: :runtime
55
+ prerelease: false
56
+ name: json_pure
57
+ version_requirements: &id003 !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ hash: 11
63
+ segments:
64
+ - 1
65
+ - 4
66
+ - 6
67
+ version: 1.4.6
68
+ requirement: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ type: :runtime
71
+ prerelease: false
72
+ name: hexy
73
+ version_requirements: &id004 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ~>
77
+ - !ruby/object:Gem::Version
78
+ hash: 25
79
+ segments:
80
+ - 0
81
+ - 1
82
+ - 1
83
+ version: 0.1.1
84
+ requirement: *id004
85
+ - !ruby/object:Gem::Dependency
86
+ type: :development
87
+ prerelease: false
88
+ name: bundler
89
+ version_requirements: &id005 !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ~>
93
+ - !ruby/object:Gem::Version
94
+ hash: 23
95
+ segments:
96
+ - 1
97
+ - 0
98
+ - 0
99
+ version: 1.0.0
100
+ requirement: *id005
101
+ - !ruby/object:Gem::Dependency
102
+ type: :development
103
+ prerelease: false
104
+ name: jeweler
105
+ version_requirements: &id006 !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ hash: 1
111
+ segments:
112
+ - 1
113
+ - 5
114
+ - 1
115
+ version: 1.5.1
116
+ requirement: *id006
117
+ description: Make load and performance testing a fun sport
118
+ email: kowsik@gmail.com
119
+ executables:
120
+ - blitz
121
+ extensions: []
122
+
123
+ extra_rdoc_files:
124
+ - LICENSE.txt
125
+ - README.rdoc
126
+ files:
127
+ - .document
128
+ - Gemfile
129
+ - Gemfile.lock
130
+ - LICENSE.txt
131
+ - README.rdoc
132
+ - Rakefile
133
+ - bin/blitz
134
+ - blitz.gemspec
135
+ - lib/blitz.rb
136
+ - lib/blitz/client.rb
137
+ - lib/blitz/command.rb
138
+ - lib/blitz/command/api.rb
139
+ - lib/blitz/command/curl.rb
140
+ - lib/blitz/command/help.rb
141
+ - lib/blitz/curl/error.rb
142
+ - lib/blitz/curl/sprint.rb
143
+ - lib/blitz/helper.rb
144
+ - test/helper.rb
145
+ - test/test_blitz.rb
146
+ has_rdoc: true
147
+ homepage: http://blitz.io
148
+ licenses:
149
+ - MIT
150
+ post_install_message:
151
+ rdoc_options: []
152
+
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ hash: 3
161
+ segments:
162
+ - 0
163
+ version: "0"
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ none: false
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ hash: 3
170
+ segments:
171
+ - 0
172
+ version: "0"
173
+ requirements: []
174
+
175
+ rubyforge_project:
176
+ rubygems_version: 1.3.7
177
+ signing_key:
178
+ specification_version: 3
179
+ summary: Make load and performance testing a fun sport
180
+ test_files:
181
+ - test/helper.rb
182
+ - test/test_blitz.rb