ant_hill 0.3.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 +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
|