zombees 0.0.1
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/.gitignore +23 -0
- data/.rspec +3 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +104 -0
- data/Guardfile +24 -0
- data/README.md +51 -0
- data/Rakefile +20 -0
- data/ab_adapter_example.rb +10 -0
- data/lib/zombees/ab_adapter.rb +119 -0
- data/lib/zombees/connection.rb +16 -0
- data/lib/zombees/honey_comb.rb +22 -0
- data/lib/zombees/queen.rb +38 -0
- data/lib/zombees/swarm.rb +59 -0
- data/lib/zombees/swarm_options.rb +18 -0
- data/lib/zombees/version.rb +3 -0
- data/lib/zombees/worker.rb +45 -0
- data/lib/zombees.rb +3 -0
- data/spec/fixtures/ab.txt +45 -0
- data/spec/integration/running_a_server_swarm_spec.rb +24 -0
- data/spec/integration/worker_spec.rb +43 -0
- data/spec/integration_spec_helper.rb +3 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/zombees/ab_adapter_spec.rb +139 -0
- data/spec/zombees/command_role_spec.rb +9 -0
- data/spec/zombees/connection_spec.rb +47 -0
- data/spec/zombees/honey_comb_spec.rb +17 -0
- data/spec/zombees/queen_spec.rb +29 -0
- data/spec/zombees/swarm_spec.rb +90 -0
- data/tourfleet +27 -0
- data/tourfleet.pub +1 -0
- data/zombee.png +0 -0
- data/zombees.gemspec +34 -0
- metadata +286 -0
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
coverage
|
6
|
+
InstalledFiles
|
7
|
+
lib/bundler/man
|
8
|
+
pkg
|
9
|
+
rdoc
|
10
|
+
spec/reports
|
11
|
+
test/tmp
|
12
|
+
test/version_tmp
|
13
|
+
tmp
|
14
|
+
|
15
|
+
# YARD artifacts
|
16
|
+
.yardoc
|
17
|
+
_yardoc
|
18
|
+
doc/
|
19
|
+
aws_config.yml
|
20
|
+
|
21
|
+
#VIM
|
22
|
+
*.swp
|
23
|
+
*.swo
|
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
tourfleet
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3-p392
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
gemspec
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
zombees (0.0.1)
|
5
|
+
celluloid (~> 0.13.0)
|
6
|
+
celluloid-pmap
|
7
|
+
colorize
|
8
|
+
fog (~> 1.8.0)
|
9
|
+
net-ssh (~> 2.5.0)
|
10
|
+
yell
|
11
|
+
|
12
|
+
GEM
|
13
|
+
specs:
|
14
|
+
builder (3.2.0)
|
15
|
+
celluloid (0.13.0)
|
16
|
+
timers (>= 1.0.0)
|
17
|
+
celluloid-pmap (0.1.0)
|
18
|
+
celluloid (~> 0.12)
|
19
|
+
coderay (1.0.9)
|
20
|
+
colorize (0.5.8)
|
21
|
+
coveralls (0.6.7)
|
22
|
+
colorize
|
23
|
+
multi_json (~> 1.3)
|
24
|
+
rest-client
|
25
|
+
simplecov (>= 0.7)
|
26
|
+
thor
|
27
|
+
diff-lcs (1.2.3)
|
28
|
+
emoji-rspec (1.0.0)
|
29
|
+
rspec (~> 2.10)
|
30
|
+
excon (0.22.1)
|
31
|
+
ffi (1.8.1)
|
32
|
+
fog (1.8.0)
|
33
|
+
builder
|
34
|
+
excon (~> 0.14)
|
35
|
+
formatador (~> 0.2.0)
|
36
|
+
mime-types
|
37
|
+
multi_json (~> 1.0)
|
38
|
+
net-scp (~> 1.0.4)
|
39
|
+
net-ssh (>= 2.1.3)
|
40
|
+
nokogiri (~> 1.5.0)
|
41
|
+
ruby-hmac
|
42
|
+
formatador (0.2.4)
|
43
|
+
guard (1.8.0)
|
44
|
+
formatador (>= 0.2.4)
|
45
|
+
listen (>= 1.0.0)
|
46
|
+
lumberjack (>= 1.0.2)
|
47
|
+
pry (>= 0.9.10)
|
48
|
+
thor (>= 0.14.6)
|
49
|
+
guard-rspec (2.6.0)
|
50
|
+
guard (>= 1.8)
|
51
|
+
rspec (~> 2.13)
|
52
|
+
listen (1.0.3)
|
53
|
+
rb-fsevent (>= 0.9.3)
|
54
|
+
rb-inotify (>= 0.9)
|
55
|
+
rb-kqueue (>= 0.2)
|
56
|
+
lumberjack (1.0.3)
|
57
|
+
method_source (0.8.1)
|
58
|
+
mime-types (1.23)
|
59
|
+
multi_json (1.7.2)
|
60
|
+
net-scp (1.0.4)
|
61
|
+
net-ssh (>= 1.99.1)
|
62
|
+
net-ssh (2.5.2)
|
63
|
+
nokogiri (1.5.9)
|
64
|
+
pry (0.9.12.1)
|
65
|
+
coderay (~> 1.0.5)
|
66
|
+
method_source (~> 0.8)
|
67
|
+
slop (~> 3.4)
|
68
|
+
rake (10.0.4)
|
69
|
+
rb-fsevent (0.9.3)
|
70
|
+
rb-inotify (0.9.0)
|
71
|
+
ffi (>= 0.5.0)
|
72
|
+
rb-kqueue (0.2.0)
|
73
|
+
ffi (>= 0.5.0)
|
74
|
+
rest-client (1.6.7)
|
75
|
+
mime-types (>= 1.16)
|
76
|
+
rspec (2.13.0)
|
77
|
+
rspec-core (~> 2.13.0)
|
78
|
+
rspec-expectations (~> 2.13.0)
|
79
|
+
rspec-mocks (~> 2.13.0)
|
80
|
+
rspec-core (2.13.1)
|
81
|
+
rspec-expectations (2.13.0)
|
82
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
83
|
+
rspec-mocks (2.13.1)
|
84
|
+
ruby-hmac (0.4.0)
|
85
|
+
simplecov (0.7.1)
|
86
|
+
multi_json (~> 1.0)
|
87
|
+
simplecov-html (~> 0.7.1)
|
88
|
+
simplecov-html (0.7.1)
|
89
|
+
slop (3.4.4)
|
90
|
+
thor (0.18.1)
|
91
|
+
timers (1.1.0)
|
92
|
+
yell (1.3.0)
|
93
|
+
|
94
|
+
PLATFORMS
|
95
|
+
ruby
|
96
|
+
|
97
|
+
DEPENDENCIES
|
98
|
+
bundler (~> 1.3)
|
99
|
+
coveralls
|
100
|
+
emoji-rspec
|
101
|
+
guard-rspec
|
102
|
+
rake
|
103
|
+
rspec
|
104
|
+
zombees!
|
data/Guardfile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'rspec' do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
|
+
|
9
|
+
# Rails example
|
10
|
+
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
11
|
+
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
12
|
+
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
13
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
14
|
+
watch('config/routes.rb') { "spec/routing" }
|
15
|
+
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
16
|
+
|
17
|
+
# Capybara features specs
|
18
|
+
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
|
19
|
+
|
20
|
+
# Turnip features and steps
|
21
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
22
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
23
|
+
end
|
24
|
+
|
data/README.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
[](https://travis-ci.org/zombees/zombees)
|
2
|
+
[](https://codeclimate.com/github/zombees/zombees)
|
3
|
+
[](https://coveralls.io/r/zombees/zombees?branch=master)
|
4
|
+
|
5
|
+

|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'zombees'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install zombees
|
20
|
+
|
21
|
+
## Example usage
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
# ab_adapter_example.rb
|
25
|
+
require 'yaml'
|
26
|
+
require 'zombees'
|
27
|
+
require 'zombees/ab_adapter'
|
28
|
+
|
29
|
+
aws_config = YAML.load_file('aws_config.yml').symbolize_keys!
|
30
|
+
|
31
|
+
adapter = Zombees::AbAdapter.new requests: 10, concurrency: 10, url: TEST_URL
|
32
|
+
result = Zombees::Queen.new(config: aws_config, worker_count: 2, command: adapter).run
|
33
|
+
puts result.inspect
|
34
|
+
```
|
35
|
+
|
36
|
+
## Contributing
|
37
|
+
|
38
|
+
1. Fork it
|
39
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
40
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
41
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
42
|
+
5. Create new Pull Request
|
43
|
+
|
44
|
+
|
45
|
+
TODO
|
46
|
+
------------
|
47
|
+
- Simple CLI
|
48
|
+
- Verbose mode
|
49
|
+
- Adapters for different command to run
|
50
|
+
- Siege
|
51
|
+
- Tourbus
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
RSpec::Core::RakeTask.new(:spec)
|
4
|
+
task default: :spec
|
5
|
+
|
6
|
+
task :push => :run_all_specs do
|
7
|
+
puts "Pushing real good"
|
8
|
+
Rake.sh 'git push origin master'
|
9
|
+
end
|
10
|
+
|
11
|
+
task :run_all_specs => :spec do
|
12
|
+
Rake.sh 'bundle exec rspec --tag @integration'
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Run all specs with code coverage'
|
16
|
+
task :coverage do
|
17
|
+
ENV['COVERAGE'] = 'true'
|
18
|
+
Rake::Task['spec'].execute
|
19
|
+
Rake::Task['run_all_specs'].execute
|
20
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'zombees'
|
3
|
+
require 'zombees/ab_adapter'
|
4
|
+
|
5
|
+
aws_config = YAML.load_file('aws_config.yml').symbolize_keys!
|
6
|
+
|
7
|
+
url = URL_TO_TEST
|
8
|
+
adapter = Zombees::AbAdapter.new requests: 10, concurrency: 10, url: "#{url}/"
|
9
|
+
result = Zombees::Queen.new(config: aws_config, worker_count: 2, command: adapter).run
|
10
|
+
puts result.inspect
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Zombees
|
2
|
+
class AbAdapter
|
3
|
+
attr_reader :config
|
4
|
+
attr_writer :command_source
|
5
|
+
attr_writer :preparer_source
|
6
|
+
attr_writer :parser_source
|
7
|
+
def initialize(config={})
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
def command_source; @command_source ||= Command.public_method(:new) end
|
12
|
+
def preparer_source; @preparer_source ||= Preparer.new end
|
13
|
+
def parser_source(output_of_commands); @parser_source ||= Parser.new(output_of_commands) end
|
14
|
+
private :command_source, :preparer_source, :parser_source
|
15
|
+
|
16
|
+
def prepare(worker)
|
17
|
+
preparer_source.prepare(worker)
|
18
|
+
end
|
19
|
+
|
20
|
+
def run(worker)
|
21
|
+
command_source.call(config).run(worker)
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse(output_of_commands)
|
25
|
+
parser_source(output_of_commands).parse
|
26
|
+
end
|
27
|
+
|
28
|
+
def aggregate(output_of_commands)
|
29
|
+
parse_result = parse(output_of_commands)
|
30
|
+
Aggregator.new(parse_result).aggregate
|
31
|
+
end
|
32
|
+
|
33
|
+
class Preparer
|
34
|
+
def prepare(worker)
|
35
|
+
worker.run_command('sudo apt-get -y update && sudo apt-get -y install apache2-utils')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Command
|
40
|
+
attr_reader :requests, :concurrency, :url, :ab_options
|
41
|
+
|
42
|
+
def initialize(options={})
|
43
|
+
options = default.merge(options)
|
44
|
+
@requests = options[:requests]
|
45
|
+
@concurrency = options[:concurrency]
|
46
|
+
@url = options[:url]
|
47
|
+
@ab_options = options[:ab_options]
|
48
|
+
end
|
49
|
+
|
50
|
+
def default
|
51
|
+
{}
|
52
|
+
end
|
53
|
+
|
54
|
+
def run(worker)
|
55
|
+
worker.run_command(command)
|
56
|
+
end
|
57
|
+
|
58
|
+
def command
|
59
|
+
"ab -r -n #{requests} -c #{concurrency} " +
|
60
|
+
%Q{-C "sessionid=fake" #{ab_options} "#{url}"}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Aggregator
|
65
|
+
attr_reader :parsed_results
|
66
|
+
def initialize(parsed_results)
|
67
|
+
@parsed_results = parsed_results
|
68
|
+
end
|
69
|
+
|
70
|
+
def sum(key)
|
71
|
+
values = parsed_results.map { |res| res[key] }.compact
|
72
|
+
values.inject(:+)
|
73
|
+
end
|
74
|
+
|
75
|
+
def average(key)
|
76
|
+
values = parsed_results.map { |res| res[key] }.compact
|
77
|
+
if total_values = values.inject(:+)
|
78
|
+
total_values / values.count
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def hash_result(key, aggregator)
|
83
|
+
value = self.send(aggregator, key)
|
84
|
+
value ? {key => value} : {}
|
85
|
+
end
|
86
|
+
|
87
|
+
def aggregate
|
88
|
+
{}.tap do |result|
|
89
|
+
result.merge! hash_result(:complete_requests, :sum)
|
90
|
+
result.merge! hash_result(:failed_requests, :sum)
|
91
|
+
result.merge! hash_result(:time_per_request, :average)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class Parser
|
97
|
+
attr_reader :command_results
|
98
|
+
def initialize(command_results)
|
99
|
+
@command_results = command_results
|
100
|
+
end
|
101
|
+
|
102
|
+
def parse
|
103
|
+
command_results.flatten.map do |command|
|
104
|
+
self.class.parse(command.stdout)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.parse(output)
|
109
|
+
output.each_line.each_with_object({}) do |line, data|
|
110
|
+
key, value = line.strip.split(/\s*:\s*/)
|
111
|
+
if key
|
112
|
+
key += " concurrent" if value =~ /concurrent/
|
113
|
+
data[key.downcase.gsub(/\s+/, '_').to_sym] = value.to_f
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'fog'
|
2
|
+
module Zombees
|
3
|
+
class Connection
|
4
|
+
extend Forwardable
|
5
|
+
def_delegator :@connection, :servers
|
6
|
+
|
7
|
+
def self.import_key_pair(config)
|
8
|
+
connection = Fog::Compute.new(config)
|
9
|
+
connection.import_key_pair(:fog_default, IO.read('tourfleet.pub')) if connection.key_pairs.get(:fog_default).nil?
|
10
|
+
end
|
11
|
+
def initialize(config)
|
12
|
+
@connection = Fog::Compute.new(config)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'zombees/worker'
|
2
|
+
require 'zombees/connection'
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
module Zombees
|
6
|
+
class HoneyComb
|
7
|
+
attr_reader :config
|
8
|
+
|
9
|
+
def worker
|
10
|
+
Worker.new(connection)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(config)
|
14
|
+
@config = config
|
15
|
+
Connection.import_key_pair(config)
|
16
|
+
end
|
17
|
+
|
18
|
+
def connection
|
19
|
+
Connection.new(config)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'zombees/worker'
|
3
|
+
require 'zombees/swarm'
|
4
|
+
require 'zombees/honey_comb'
|
5
|
+
require 'yell'
|
6
|
+
|
7
|
+
module Zombees
|
8
|
+
class Queen
|
9
|
+
include Yell::Loggable
|
10
|
+
attr_reader :config, :worker_count, :command, :swarm
|
11
|
+
attr_writer :swarm_source
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
@config = options.fetch(:config)
|
15
|
+
@worker_count = options.fetch(:worker_count)
|
16
|
+
@command = options.fetch(:command)
|
17
|
+
@swarm = options[:swarm]
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
logger.info "A swarm of zombie bees gathers..."
|
22
|
+
swarm.run
|
23
|
+
end
|
24
|
+
|
25
|
+
def swarm
|
26
|
+
swarm_source.call(SwarmOptions.new(
|
27
|
+
worker_count: worker_count,
|
28
|
+
command: command,
|
29
|
+
honey_comb: HoneyComb.new(config)))
|
30
|
+
end
|
31
|
+
|
32
|
+
def swarm_source
|
33
|
+
@swarm_source ||= Swarm.public_method(:new)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'celluloid'
|
2
|
+
require 'celluloid/pmap'
|
3
|
+
require 'yell'
|
4
|
+
require 'zombees/swarm_options'
|
5
|
+
require 'forwardable'
|
6
|
+
|
7
|
+
module Zombees
|
8
|
+
class Swarm
|
9
|
+
attr_reader :worker_count, :command, :honey_comb
|
10
|
+
include Yell::Loggable
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
def initialize(options, population=nil)
|
14
|
+
@worker_count = options.worker_count
|
15
|
+
@adapter = options.command
|
16
|
+
@honey_comb = options.honey_comb
|
17
|
+
@population = population
|
18
|
+
end
|
19
|
+
|
20
|
+
def breed
|
21
|
+
logger.info "Resurrecting the bees population of #{worker_count}"
|
22
|
+
@population ||= worker_count.downto(1).pmap do |index|
|
23
|
+
worker = honey_comb.worker
|
24
|
+
begin
|
25
|
+
logger.info "Bee #{ index } is getting ready to fight"
|
26
|
+
worker.bootstrap.tap { |w| @adapter.prepare(w) }
|
27
|
+
rescue => e
|
28
|
+
logger.error "ARGH! Bee #{ index } stayed dead: #{e.inspect}"
|
29
|
+
worker.shutdown(e)
|
30
|
+
end
|
31
|
+
worker
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def population
|
37
|
+
breed
|
38
|
+
end
|
39
|
+
|
40
|
+
def run
|
41
|
+
logger.info "The swarm lurches toward the target..."
|
42
|
+
results = population.pmap do |worker|
|
43
|
+
begin
|
44
|
+
logger.info "A zombie bee is attacking the target!"
|
45
|
+
@adapter.run(worker)
|
46
|
+
rescue => e
|
47
|
+
logger.error "A zombie bee can't reach the brains due to #{e.message}"
|
48
|
+
logger.debug e.backtrace
|
49
|
+
ensure
|
50
|
+
logger.info "A zombie bee is full of brains... Going back to the grave"
|
51
|
+
#logger.debug "worker: #{ worker.object_id } server: #{ worker.server.inspect }"
|
52
|
+
worker.shutdown
|
53
|
+
end
|
54
|
+
end
|
55
|
+
logger.info "Digesting acquired brains"
|
56
|
+
@adapter.aggregate(results)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class SwarmOptions
|
2
|
+
attr_reader :options
|
3
|
+
def initialize(options)
|
4
|
+
@options = options
|
5
|
+
end
|
6
|
+
|
7
|
+
def worker_count
|
8
|
+
options[:worker_count]
|
9
|
+
end
|
10
|
+
|
11
|
+
def command
|
12
|
+
options[:command]
|
13
|
+
end
|
14
|
+
|
15
|
+
def honey_comb
|
16
|
+
options[:honey_comb]
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'celluloid'
|
3
|
+
require 'celluloid/pmap'
|
4
|
+
require 'zombees/connection'
|
5
|
+
require 'fog'
|
6
|
+
require 'yell'
|
7
|
+
|
8
|
+
module Zombees
|
9
|
+
class Worker
|
10
|
+
include Celluloid
|
11
|
+
include Yell::Loggable
|
12
|
+
attr_reader :config
|
13
|
+
attr_reader :server
|
14
|
+
extend Forwardable
|
15
|
+
def_delegator :@server, :ready?
|
16
|
+
|
17
|
+
def initialize(connection)
|
18
|
+
@connection = connection
|
19
|
+
end
|
20
|
+
|
21
|
+
def bootstrap
|
22
|
+
@server = @connection.servers.bootstrap(
|
23
|
+
private_key_path: 'tourfleet',
|
24
|
+
key_name: :fog_default,
|
25
|
+
username: 'ubuntu'
|
26
|
+
)
|
27
|
+
|
28
|
+
@server.wait_for { ready? }
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_command(command)
|
33
|
+
result = @server.ssh(command)
|
34
|
+
result
|
35
|
+
end
|
36
|
+
|
37
|
+
def shutdown
|
38
|
+
if @server && @server.ready?
|
39
|
+
@server.destroy
|
40
|
+
else
|
41
|
+
logger.error "zombie bee is too badly damaged to get to the graveyard"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/zombees.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
|
2
|
+
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
|
3
|
+
Licensed to The Apache Software Foundation, http://www.apache.org/
|
4
|
+
|
5
|
+
Benchmarking www.google.com (be patient)...Send request failed!
|
6
|
+
..done
|
7
|
+
|
8
|
+
|
9
|
+
Server Software: gws
|
10
|
+
Server Hostname: www.google.com
|
11
|
+
Server Port: 80
|
12
|
+
|
13
|
+
Document Path: /
|
14
|
+
Document Length: 10740 bytes
|
15
|
+
|
16
|
+
Concurrency Level: 10
|
17
|
+
Time taken for tests: 0.083 seconds
|
18
|
+
Complete requests: 10
|
19
|
+
Failed requests: 11
|
20
|
+
(Connect: 0, Receive: 3, Length: 8, Exceptions: 0)
|
21
|
+
Write errors: 1
|
22
|
+
Total transferred: 103128 bytes
|
23
|
+
HTML transferred: 96504 bytes
|
24
|
+
Requests per second: 120.06 [#/sec] (mean)
|
25
|
+
Time per request: 83.295 [ms] (mean)
|
26
|
+
Time per request: 8.329 [ms] (mean, across all concurrent requests)
|
27
|
+
Transfer rate: 1209.09 [Kbytes/sec] received
|
28
|
+
|
29
|
+
Connection Times (ms)
|
30
|
+
min mean[+/-sd] median max
|
31
|
+
Connect: 13 16 9.2 13 42
|
32
|
+
Processing: 40 67 9.4 70 70
|
33
|
+
Waiting: 0 63 22.0 69 70
|
34
|
+
Total: 82 83 0.2 83 83
|
35
|
+
|
36
|
+
Percentage of the requests served within a certain time (ms)
|
37
|
+
50% 83
|
38
|
+
66% 83
|
39
|
+
75% 83
|
40
|
+
80% 83
|
41
|
+
90% 83
|
42
|
+
95% 83
|
43
|
+
98% 83
|
44
|
+
99% 83
|
45
|
+
100% 83 (longest request)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'integration_spec_helper'
|
2
|
+
require 'zombees'
|
3
|
+
require 'zombees/ab_adapter'
|
4
|
+
|
5
|
+
describe 'Running 3 servers', integration: true do
|
6
|
+
let(:config) {{ provider: 'AWS', aws_access_key_id: 'foo', aws_secret_access_key: 'bar' }}
|
7
|
+
|
8
|
+
class Fog::SSH::Mock
|
9
|
+
alias_method :original_run, :run
|
10
|
+
|
11
|
+
def run(commands, &blk)
|
12
|
+
Array(commands).map do |command|
|
13
|
+
result = Fog::SSH::Result.new(command)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'runs a command in parallel on all servers when they are ready' do
|
19
|
+
queen = Zombees::Queen.new(worker_count: 3, command: Zombees::AbAdapter.new, config: config)
|
20
|
+
queen.run
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|