ant_hill 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/ant_hill.gemspec +29 -0
- data/bin/spawn_queen +14 -0
- data/lib/ant_hill.rb +36 -0
- data/lib/ant_hill/ant.rb +126 -0
- data/lib/ant_hill/ant_colony.rb +212 -0
- data/lib/ant_hill/configuration.rb +154 -0
- data/lib/ant_hill/connection_pool.rb +98 -0
- data/lib/ant_hill/connections/ssh_connection.rb +67 -0
- data/lib/ant_hill/creep.rb +301 -0
- data/lib/ant_hill/creep_modifier.rb +150 -0
- data/lib/ant_hill/log.rb +35 -0
- data/lib/ant_hill/queen.rb +334 -0
- data/lib/ant_hill/version.rb +4 -0
- data/lib/tasks/ant_hill.rake +48 -0
- data/spec/ant_hill/ant_colony_spec.rb +118 -0
- data/spec/ant_hill/ant_spec.rb +101 -0
- data/spec/ant_hill/configuration_spec.rb +201 -0
- data/spec/ant_hill/creep_modifier_spec.rb +30 -0
- data/spec/ant_hill/creep_spec.rb +165 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/config.yml +43 -0
- metadata +117 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'ant_hill'
|
2
|
+
|
3
|
+
namespace :ant_hill do
|
4
|
+
task :add_colony do
|
5
|
+
host = ENV['drb_host'] || 'localhost'
|
6
|
+
AntHill::Queen.create_colony ARGV[1..-1], host
|
7
|
+
end
|
8
|
+
|
9
|
+
task :kill_colony do
|
10
|
+
host = ENV['drb_host'] || 'localhost'
|
11
|
+
AntHill::Queen.kill_colony ARGV[1..-1], host
|
12
|
+
end
|
13
|
+
|
14
|
+
task :monitor do
|
15
|
+
queen = AntHill::Queen.drb_queen(host)
|
16
|
+
while true
|
17
|
+
sleep 2
|
18
|
+
print "\e[2J\e[f"
|
19
|
+
puts "Creep size: #{queen.creeps.size}"
|
20
|
+
puts "Ants left: #{queen.size}"
|
21
|
+
queen.creeps.each{|creep|
|
22
|
+
puts creep.to_s
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
task :execute_each do
|
28
|
+
threads = []
|
29
|
+
AntHill::Queen.creeps(host).each{|creep|
|
30
|
+
threads << Thread.new do
|
31
|
+
creep.exec!(ENV['command'])
|
32
|
+
end
|
33
|
+
}
|
34
|
+
threads.each{ |t| t.join}
|
35
|
+
end
|
36
|
+
|
37
|
+
task :suspend_queen do
|
38
|
+
AntHill::Queen.drb_queen(host).suspend
|
39
|
+
end
|
40
|
+
|
41
|
+
task :release_queen do
|
42
|
+
AntHill::Queen.drb_queen(host).release
|
43
|
+
end
|
44
|
+
|
45
|
+
def host
|
46
|
+
ENV['drb_host'] || 'localhost'
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module AntHill
|
4
|
+
describe AntColony do
|
5
|
+
let(:params){ {"type" => "type", "a" => 1, "b" => 2 } }
|
6
|
+
let(:colony){ AntColony.new(params) }
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
config = double("config")
|
10
|
+
config.stub(:creep_modifier_class){|type| "Creep modifier for #{type}" }
|
11
|
+
config.stub(:log_level){ :log_level }
|
12
|
+
config.stub(:log_dir){ log_dir }
|
13
|
+
config.stub(:init_time){ Time.now}
|
14
|
+
Configuration.stub(:config){config}
|
15
|
+
|
16
|
+
logger = double("logger")
|
17
|
+
Log.stub(:logger_for){logger}
|
18
|
+
end
|
19
|
+
|
20
|
+
context "#initialize" do
|
21
|
+
it "should store params" do
|
22
|
+
colony.params.should eql(params)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "#type" do
|
27
|
+
it "should return type from params" do
|
28
|
+
colony.type.should eql("type")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "#creep_modifier_class" do
|
33
|
+
it "should return creep modifier class for type" do
|
34
|
+
Configuration.config.should_receive(:creep_modifier_class).with("type").and_return("Creep modifier for type")
|
35
|
+
colony.creep_modifier_class.should eql("Creep modifier for type")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should log error if no such type defined" do
|
39
|
+
test_params = params
|
40
|
+
test_params['type']="wrong_type"
|
41
|
+
|
42
|
+
colony = AntColony.new(params)
|
43
|
+
Configuration.config.should_receive(:creep_modifier_class).with("wrong_type").and_return(nil)
|
44
|
+
Log.logger_for.should_receive(:error).with("Colony will die without creep modifier ;(")
|
45
|
+
colony.creep_modifier_class.should be_nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "#spoiled?" do
|
50
|
+
it "should be spoiled if no creep modifier class was set" do
|
51
|
+
test_params = params
|
52
|
+
test_params['type']="wrong_type"
|
53
|
+
colony = AntColony.new(params)
|
54
|
+
colony.spoiled?.should be_true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "#get_ants" do
|
59
|
+
it "should add self params to all ants " do
|
60
|
+
colony.stub(:search_ants){ [{:param1 => 1, :param2 => 2},{ :param1 => 3, :param2 => 2}] }
|
61
|
+
ants = colony.get_ants
|
62
|
+
ants.should_not be_empty
|
63
|
+
ants.each{|ant|
|
64
|
+
ant.params['type'].should eql('type')
|
65
|
+
ant.params['a'].should eql(1)
|
66
|
+
ant.params['b'].should eql(2)
|
67
|
+
ant.params[:param2].should eql(2)
|
68
|
+
}
|
69
|
+
ants[0].params[:param1].should eql(1)
|
70
|
+
ants[1].params[:param1].should eql(3)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should call after_search" do
|
74
|
+
colony.stub(:search_ants){ [{:param1 => 1, :param2 => 2},{ :param1 => 3, :param2 => 2}] }
|
75
|
+
colony.should_receive(:after_search)
|
76
|
+
colony.get_ants
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should log error if something's happened" do
|
80
|
+
colony.stub(:search_ants){ raise }
|
81
|
+
Log.logger_for.should_receive(:error).with(/Error while processing search ants for colony/)
|
82
|
+
ants = colony.get_ants
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "#is_it_me?" do
|
87
|
+
it "should return true if all params matches" do
|
88
|
+
colony.is_it_me?({"type"=> "type", "a" => 1}).should be_true
|
89
|
+
end
|
90
|
+
it "should return false if at least one param doesn't match" do
|
91
|
+
colony.is_it_me?({"type"=> "type", "a" => 2}).should be_false
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "#not_finished" do
|
96
|
+
it "should return not finished ants" do
|
97
|
+
ant1 = double("ant1")
|
98
|
+
ant2 = double("ant2")
|
99
|
+
ant3 = double("ant3")
|
100
|
+
ant4 = double("ant4")
|
101
|
+
ant1.stub(:finished?){true}
|
102
|
+
ant2.stub(:finished?){false}
|
103
|
+
ant3.stub(:finished?){true}
|
104
|
+
ant4.stub(:finished?){true}
|
105
|
+
colony.stub(:ants){ [ant1, ant2, ant3, ant4] }
|
106
|
+
colony.not_finished.should eql([ant1, ant3, ant4])
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
context "#type" do
|
111
|
+
it "should return type from params" do
|
112
|
+
colony.params['type'].should eql("type")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module AntHill
|
4
|
+
describe Ant do
|
5
|
+
before :each do
|
6
|
+
config = double("config")
|
7
|
+
config.stub(:creep_modifier_class){|type| "Creep modifier for #{type}" }
|
8
|
+
config.stub(:log_level){ :log_level }
|
9
|
+
config.stub(:log_dir){ log_dir }
|
10
|
+
config.stub(:init_time){ Time.at(0)}
|
11
|
+
Configuration.stub(:config){config}
|
12
|
+
|
13
|
+
logger = double("logger")
|
14
|
+
Log.stub(:logger_for){logger}
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:ant_colony){
|
18
|
+
ac = double("ant_colony")
|
19
|
+
ac.stub(:type){ "type" }
|
20
|
+
ac.stub(:params){ {"type" => "type", "a" => 1, "b" => 2} }
|
21
|
+
cmc = double(:creep_modifier_class)
|
22
|
+
cm = double(:creep_modifier)
|
23
|
+
cmc.stub(:change_time_for_param){|param|
|
24
|
+
case param
|
25
|
+
when 'a' then 1
|
26
|
+
when 'b' then 2
|
27
|
+
else
|
28
|
+
0
|
29
|
+
end
|
30
|
+
|
31
|
+
}
|
32
|
+
cmc.stub(:new){ cm}
|
33
|
+
ac.stub(:creep_modifier_class){cmc}
|
34
|
+
ac.stub(:change_time_for_param){|param|
|
35
|
+
case param
|
36
|
+
when 'a' then 1
|
37
|
+
when 'b' then 2
|
38
|
+
else
|
39
|
+
0
|
40
|
+
end
|
41
|
+
|
42
|
+
}
|
43
|
+
ac
|
44
|
+
}
|
45
|
+
|
46
|
+
let(:ant){Ant.new( { "param1" => "params", "param2" => "param2"}, ant_colony )}
|
47
|
+
|
48
|
+
context "#initialize" do
|
49
|
+
it "should have colony" do
|
50
|
+
ant.colony.should eql(ant_colony)
|
51
|
+
end
|
52
|
+
it "should merge colony and self params" do
|
53
|
+
ant.params.should eql({ "type" => "type", "a" => 1, "b" => 2, "param1" => "params", "param2" => "param2" })
|
54
|
+
end
|
55
|
+
it "should have status not_started" do
|
56
|
+
ant.status.should eql(:not_started)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "#to_s" do
|
61
|
+
it "should return string of params" do
|
62
|
+
ant.to_s.should eql(ant.params.inspect.to_s)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "#change_status" do
|
67
|
+
it "should set status" do
|
68
|
+
ant.change_status(:started)
|
69
|
+
ant.status.should eql(:started)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "#priority" do
|
74
|
+
it "should have higher priority for matches params" do
|
75
|
+
ant.priority({}).should < ant.priority({"a" => 1})
|
76
|
+
ant.priority({"a" => 1}).should < ant.priority({"a" => 1, "b" => 2})
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should increase priority on sum values defined for param in change_time_for_param method" do
|
80
|
+
ant.priority({ "a" => 1 }).should be_within(0.0001).of( ant.priority({"a" => 0}) + 1 )
|
81
|
+
ant.priority({ "b" => 2 }).should be_within(0.0001).of( ant.priority({"b" => 0}) + 2 )
|
82
|
+
|
83
|
+
end
|
84
|
+
it "should return same value for same params " do
|
85
|
+
ant.priority({ "b" => 2 }).should be_within(0.0001).of( ant.priority({"b" => 2}) )
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
context "#finished?" do
|
91
|
+
it "should return false if ant status is finished" do
|
92
|
+
ant.stub(:status){:in_progress}
|
93
|
+
ant.finished?.should be_false
|
94
|
+
end
|
95
|
+
it "should return true if ant status is finished" do
|
96
|
+
ant.stub(:status){:finished}
|
97
|
+
ant.finished?.should be_true
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module AntHill
|
3
|
+
describe Configuration do
|
4
|
+
|
5
|
+
let(:hash){
|
6
|
+
{
|
7
|
+
'basedir' => "a",
|
8
|
+
'lib_path' => 'b',
|
9
|
+
'sleep_interval' => 1,
|
10
|
+
'log_dir' => 'some_dir',
|
11
|
+
'log_level' => ':error',
|
12
|
+
'types' => {
|
13
|
+
'a' => {
|
14
|
+
'ant_colony_class' => 'String',
|
15
|
+
'creep_modifier_class' => 'Object'
|
16
|
+
},
|
17
|
+
'b' => {
|
18
|
+
'ant_colony_class' => 'Hash',
|
19
|
+
'creep_modifier_class' => 'String'
|
20
|
+
},
|
21
|
+
'c' => {
|
22
|
+
'ant_colony_class' => 'SomeClass',
|
23
|
+
'creep_modifier_class' => 'SomeOtherClass'
|
24
|
+
}
|
25
|
+
},
|
26
|
+
'default_type' => 'a',
|
27
|
+
'log_path' => 'log_path',
|
28
|
+
'creeps' => [
|
29
|
+
{ 'name' => 1, 'host' => 'host', 'login' => 'login', 'password' => 'password'},
|
30
|
+
{ 'name' => 2, 'host' => 'host2', 'login' => 'login2', 'password' => 'password2'}
|
31
|
+
]
|
32
|
+
|
33
|
+
}
|
34
|
+
}
|
35
|
+
before(:each) do
|
36
|
+
YAML.stub(:load_file) {|filename| hash }
|
37
|
+
Time.stub(:now) { Time.at(0) }
|
38
|
+
File.stub(:join) { 'rubygems'}
|
39
|
+
logger = double("logger")
|
40
|
+
Log.stub(:logger_for){logger}
|
41
|
+
end
|
42
|
+
|
43
|
+
let(:config) { Configuration.new }
|
44
|
+
|
45
|
+
let(:config_parsed){
|
46
|
+
c = Configuration.new
|
47
|
+
c.parse_yaml("config.yml")
|
48
|
+
c
|
49
|
+
}
|
50
|
+
|
51
|
+
context "#initialize" do
|
52
|
+
it "should set init_time" do
|
53
|
+
config.init_time == Time.at(0)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "#parse_yaml" do
|
58
|
+
it "should fail if file not exists" do
|
59
|
+
STDERR.stub(:puts)
|
60
|
+
STDERR.should_receive(:puts).with(/Couldn't find config file/)
|
61
|
+
YAML.stub(:load_file){raise}
|
62
|
+
lambda{ config.parse_yaml("config.yml") }.should raise_error(SystemExit)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should load configuration" do
|
66
|
+
config.parse_yaml("config.yml")
|
67
|
+
config.instance_variable_get("@configuration").should_not eql({})
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "#require_libs" do
|
72
|
+
it "should require file base_dir+libpath" do
|
73
|
+
config_parsed.stub(:require){nil}
|
74
|
+
config_parsed.should_receive(:require).with("rubygems")
|
75
|
+
config_parsed.require_libs
|
76
|
+
end
|
77
|
+
it "should require file base_dir+libpath" do
|
78
|
+
config_parsed.stub(:require){ raise LoadError }
|
79
|
+
config_parsed.should_receive(:require).with("rubygems")
|
80
|
+
STDERR.should_receive(:puts).with(/Configuration file is invalid! No such file exists rubygems/)
|
81
|
+
config_parsed.require_libs
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "#validate" do
|
86
|
+
|
87
|
+
context "with valid config" do
|
88
|
+
it "should not exit if config is valid" do
|
89
|
+
lambda{config_parsed.validate}.should_not raise_error(SystemExit)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
context "with invalid config" do
|
93
|
+
before(:each) do
|
94
|
+
STDERR.stub(:puts)
|
95
|
+
end
|
96
|
+
%w{ basedir lib_path types creeps log_dir log_level }.each do |key|
|
97
|
+
it "should log error and exit if #{key} unset" do
|
98
|
+
new_hash = hash.clone
|
99
|
+
new_hash.delete(key)
|
100
|
+
YAML.stub(:load_file){|filename| new_hash }
|
101
|
+
c = Configuration.new
|
102
|
+
c.parse_yaml("config.yml")
|
103
|
+
STDERR.should_receive(:puts).with(/Configuration file is invalid! Pls. define .* keys in it/)
|
104
|
+
lambda{c.validate}.should raise_error(SystemExit)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
%w{ types creeps }.each do |key|
|
109
|
+
it "should log error and exit #{key} has no children" do
|
110
|
+
new_hash = hash.clone
|
111
|
+
new_hash[key] = []
|
112
|
+
YAML.stub(:load_file){|filename| new_hash }
|
113
|
+
c = Configuration.new
|
114
|
+
c.parse_yaml("config.yml")
|
115
|
+
STDERR.should_receive(:puts).with(/Configuration file is invalid! Pls. define at least one .* in #{key} section/)
|
116
|
+
lambda{c.validate}.should raise_error(SystemExit)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context "#creep_modifier_class" do
|
123
|
+
it "should return creep_modifier_class from config if type specified" do
|
124
|
+
config_parsed.creep_modifier_class('b').should eql(String)
|
125
|
+
end
|
126
|
+
it "should return creep_modifier_class from config for default type if not type specified" do
|
127
|
+
config_parsed.creep_modifier_class.should eql(Object)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "#ant_colony_class" do
|
132
|
+
it "should return creep_modifier_class from config if type specified" do
|
133
|
+
config_parsed.ant_colony_class('b').should eql(Hash)
|
134
|
+
end
|
135
|
+
it "should return creep_modifier_class from config for default type if not type specified" do
|
136
|
+
config_parsed.ant_colony_class.should eql(String)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "#get_class_by_type_and_object" do
|
141
|
+
it "should log error if no class name defiend for given type and object" do
|
142
|
+
Log.logger_for.should_receive(:error).with(/No class configuration defined for object and type a/)
|
143
|
+
Log.logger_for.should_receive(:error).with(/No class configuration defined for String and type d/)
|
144
|
+
config_parsed.get_class_by_type_and_object('a', 'object')
|
145
|
+
config_parsed.get_class_by_type_and_object('d', 'String')
|
146
|
+
end
|
147
|
+
it "should log error if no specified class defiend" do
|
148
|
+
Log.logger_for.should_receive(:error).with("No such class defined: SomeClass")
|
149
|
+
config_parsed.get_class_by_type_and_object('c', 'ant_colony_class')
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "#[]" do
|
154
|
+
it "should access to configuration with [] method" do
|
155
|
+
config_parsed['default_type'].should eql('a')
|
156
|
+
config_parsed['default_type'].should eql('a')
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "#method_missing" do
|
161
|
+
it "should return default_type from config" do
|
162
|
+
config_parsed.default_type.should eql("a")
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context "Configuration#config" do
|
167
|
+
let(:config_double){
|
168
|
+
config_double = double("config")
|
169
|
+
config_double.stub(:parse_yaml){|file| }
|
170
|
+
config_double.stub(:validate)
|
171
|
+
config_double.stub(:require_libs)
|
172
|
+
config_double
|
173
|
+
}
|
174
|
+
before :each do
|
175
|
+
Configuration.method(:remove_class_variable).call(:@@config) if Configuration.class_variable_defined?(:@@config)
|
176
|
+
end
|
177
|
+
it "should return same object every time" do
|
178
|
+
Configuration.stub(:new){config_double}
|
179
|
+
config_double.should_receive(:parse_yaml)
|
180
|
+
config_double.should_receive(:validate)
|
181
|
+
config_double.should_receive(:require_libs)
|
182
|
+
Configuration.config.should be_equal(Configuration.config)
|
183
|
+
end
|
184
|
+
it "should use passed filename for parsing" do
|
185
|
+
Configuration.stub(:new){ config_double }
|
186
|
+
config_double.should_receive(:parse_yaml).with("filename.yml")
|
187
|
+
config_double.should_receive(:require_libs)
|
188
|
+
config_double.should_receive(:validate)
|
189
|
+
Configuration.config("filename.yml")
|
190
|
+
end
|
191
|
+
it "should use ARGV[0] as filename for parsing" do
|
192
|
+
Configuration.stub(:new){ config_double }
|
193
|
+
ARGV[0]='filename.yml'
|
194
|
+
config_double.should_receive(:parse_yaml).with("filename.yml")
|
195
|
+
config_double.should_receive(:require_libs)
|
196
|
+
config_double.should_receive(:validate)
|
197
|
+
Configuration.config
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|