right_agent 0.10.13 → 0.13.5
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/lib/right_agent.rb +2 -0
- data/lib/right_agent/actor.rb +45 -10
- data/lib/right_agent/actor_registry.rb +5 -5
- data/lib/right_agent/actors/agent_manager.rb +4 -4
- data/lib/right_agent/agent.rb +97 -37
- data/lib/right_agent/agent_tag_manager.rb +1 -2
- data/lib/right_agent/command/command_io.rb +1 -3
- data/lib/right_agent/command/command_runner.rb +9 -3
- data/lib/right_agent/dispatched_cache.rb +110 -0
- data/lib/right_agent/dispatcher.rb +119 -180
- data/lib/right_agent/history.rb +136 -0
- data/lib/right_agent/log.rb +6 -3
- data/lib/right_agent/monkey_patches/ruby_patch.rb +0 -1
- data/lib/right_agent/pid_file.rb +1 -1
- data/lib/right_agent/platform.rb +2 -2
- data/lib/right_agent/platform/linux.rb +8 -1
- data/lib/right_agent/platform/windows.rb +1 -1
- data/lib/right_agent/sender.rb +57 -41
- data/right_agent.gemspec +4 -4
- data/spec/actor_registry_spec.rb +7 -8
- data/spec/actor_spec.rb +87 -24
- data/spec/agent_spec.rb +107 -8
- data/spec/command/command_runner_spec.rb +12 -1
- data/spec/dispatched_cache_spec.rb +142 -0
- data/spec/dispatcher_spec.rb +110 -129
- data/spec/history_spec.rb +234 -0
- data/spec/idempotent_request_spec.rb +1 -1
- data/spec/log_spec.rb +15 -0
- data/spec/operation_result_spec.rb +4 -2
- data/spec/platform/darwin_spec.rb +13 -0
- data/spec/platform/linux_spec.rb +38 -0
- data/spec/platform/platform_spec.rb +46 -51
- data/spec/platform/windows_spec.rb +13 -0
- data/spec/sender_spec.rb +81 -38
- metadata +12 -9
- data/lib/right_agent/monkey_patches/ruby_patch/singleton_patch.rb +0 -45
- data/spec/platform/darwin.rb +0 -11
- data/spec/platform/linux.rb +0 -23
- data/spec/platform/windows.rb +0 -11
@@ -0,0 +1,234 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-2011 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
24
|
+
require 'tmpdir'
|
25
|
+
|
26
|
+
describe RightScale::History do
|
27
|
+
|
28
|
+
# Mock Time.now with well-defined interval
|
29
|
+
def mock_time(size = 20, interval = 10)
|
30
|
+
now = Time.at(1000000)
|
31
|
+
list = []
|
32
|
+
size.times { list << (now += interval) }
|
33
|
+
flexmock(Time).should_receive(:now).and_return(*list)
|
34
|
+
end
|
35
|
+
|
36
|
+
before(:each) do
|
37
|
+
@identity = "rs-agent-1-1"
|
38
|
+
@pid = Process.pid
|
39
|
+
@now = Time.at(1000000)
|
40
|
+
flexmock(Time).should_receive(:now).and_return(@now).by_default
|
41
|
+
FileUtils.mkdir_p(@test_dir = File.join(RightScale::Platform.filesystem.temp_dir, 'history_test'))
|
42
|
+
RightScale::AgentConfig.pid_dir = @test_dir
|
43
|
+
@history = RightScale::History.new(@identity)
|
44
|
+
end
|
45
|
+
|
46
|
+
after(:each) do
|
47
|
+
FileUtils.remove_dir(@test_dir)
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "update" do
|
51
|
+
|
52
|
+
it "should store event in history file" do
|
53
|
+
@history.update("some event").should be_true
|
54
|
+
@history.load.should == [{"time" => 1000000, "pid" => @pid, "event" => "some event"}]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should store event in history file following previous event" do
|
58
|
+
@history.update("some event").should be_true
|
59
|
+
flexmock(Time).should_receive(:now).and_return(@now += 10)
|
60
|
+
@history.update("another event").should be_true
|
61
|
+
@history.load.should == [{"time" => 1000000, "pid" => @pid, "event" => "some event"},
|
62
|
+
{"time" => 1000010, "pid" => @pid, "event" => "another event"}]
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "load" do
|
68
|
+
|
69
|
+
it "should load no events if there is no history" do
|
70
|
+
@history.load.should == []
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should load events from history file" do
|
74
|
+
@history.update("some event").should be_true
|
75
|
+
@history.load.should == [{"time" => 1000000, "pid" => @pid, "event" => "some event"}]
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "analyze_service" do
|
81
|
+
|
82
|
+
it "should indicate no uptime if history empty" do
|
83
|
+
@history.analyze_service.should == {:uptime => 0, :total_uptime => 0}
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should indicate no uptime if not yet running" do
|
87
|
+
mock_time
|
88
|
+
@history.update("start")
|
89
|
+
@history.analyze_service.should == {:uptime => 0, :total_uptime => 0}
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should measure uptime starting from last run time" do
|
93
|
+
mock_time
|
94
|
+
@history.update("start")
|
95
|
+
@history.update("run")
|
96
|
+
@history.analyze_service.should == {:uptime => 10, :total_uptime => 10}
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should not count initial start as a restart" do
|
100
|
+
@history.update("start")
|
101
|
+
@history.update("run")
|
102
|
+
@history.analyze_service.should == {:uptime => 0, :total_uptime => 0}
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should count restarts" do
|
106
|
+
@history.update("start")
|
107
|
+
@history.update("stop")
|
108
|
+
@history.update("start")
|
109
|
+
@history.analyze_service.should == {:uptime => 0, :total_uptime => 0, :restarts => 1, :graceful_exits => 0}
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should ignore repeated stops when counting restarts" do
|
113
|
+
@history.update("start")
|
114
|
+
@history.update("stop")
|
115
|
+
@history.update("stop")
|
116
|
+
@history.update("start")
|
117
|
+
@history.analyze_service.should == {:uptime => 0, :total_uptime => 0, :restarts => 1, :graceful_exits => 0}
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should record number of graceful exits if there are restarts" do
|
121
|
+
@history.update("start")
|
122
|
+
@history.update("run")
|
123
|
+
@history.update("stop")
|
124
|
+
@history.update("graceful exit")
|
125
|
+
@history.update("start")
|
126
|
+
@history.update("stop")
|
127
|
+
@history.update("start")
|
128
|
+
@history.update("run")
|
129
|
+
@history.update("stop")
|
130
|
+
@history.update("graceful exit")
|
131
|
+
@history.update("start")
|
132
|
+
@history.update("run")
|
133
|
+
@history.analyze_service.should == {:uptime => 0, :total_uptime => 0, :restarts => 3, :graceful_exits => 2}
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should measure total uptime across restarts and include current uptime" do
|
137
|
+
mock_time
|
138
|
+
@history.update("start")
|
139
|
+
@history.update("run")
|
140
|
+
@history.update("stop")
|
141
|
+
@history.update("graceful exit")
|
142
|
+
@history.update("start")
|
143
|
+
@history.update("stop")
|
144
|
+
@history.update("start")
|
145
|
+
@history.update("run")
|
146
|
+
@history.update("stop")
|
147
|
+
@history.update("graceful exit")
|
148
|
+
@history.update("start")
|
149
|
+
@history.update("run")
|
150
|
+
@history.analyze_service.should == {:uptime => 10, :total_uptime => 30, :restarts => 3, :graceful_exits => 2}
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should count crashes" do
|
154
|
+
@history.update("start")
|
155
|
+
@history.update("start")
|
156
|
+
@history.analyze_service.should == {:uptime => 0, :total_uptime => 0, :crashes => 1, :last_crash_time => 1000000}
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should record last crash age if there are crashes" do
|
160
|
+
mock_time
|
161
|
+
@history.update("start")
|
162
|
+
@history.update("start")
|
163
|
+
@history.analyze_service.should == {:uptime => 0, :total_uptime => 0, :crashes => 1, :last_crash_time => 1000020}
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should count restarts and crashes" do
|
167
|
+
mock_time
|
168
|
+
@history.update("start")
|
169
|
+
@history.update("run")
|
170
|
+
@history.update("stop")
|
171
|
+
@history.update("graceful exit")
|
172
|
+
@history.update("start")
|
173
|
+
@history.update("start")
|
174
|
+
@history.update("stop")
|
175
|
+
@history.update("start")
|
176
|
+
@history.update("run")
|
177
|
+
@history.update("stop")
|
178
|
+
@history.update("graceful exit")
|
179
|
+
@history.update("start")
|
180
|
+
@history.update("run")
|
181
|
+
@history.update("start")
|
182
|
+
@history.update("run")
|
183
|
+
@history.analyze_service.should == {:uptime => 10, :total_uptime => 40, :restarts => 3, :graceful_exits => 2,
|
184
|
+
:crashes => 2, :last_crash_time => 1000140}
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should ignore unrecognized events" do
|
188
|
+
@history.update("other event")
|
189
|
+
@history.update("start")
|
190
|
+
@history.update("other event")
|
191
|
+
@history.update("stop")
|
192
|
+
@history.update("start")
|
193
|
+
@history.update("other event")
|
194
|
+
@history.analyze_service.should == {:uptime => 0, :total_uptime => 0, :restarts => 1, :graceful_exits => 0}
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should not re-analyze if there are no new events" do
|
198
|
+
mock_time
|
199
|
+
@history.update("start")
|
200
|
+
@history.update("run")
|
201
|
+
@history.analyze_service.should == {:uptime => 10, :total_uptime => 10}
|
202
|
+
flexmock(@history).should_receive(:load).never
|
203
|
+
@history.analyze_service
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should update uptime and total_uptime even if do not do re-analyze" do
|
207
|
+
mock_time
|
208
|
+
@history.update("start")
|
209
|
+
@history.update("run")
|
210
|
+
@history.update("start")
|
211
|
+
@history.update("run")
|
212
|
+
@history.update("stop")
|
213
|
+
@history.update("graceful exit")
|
214
|
+
@history.update("start")
|
215
|
+
@history.update("run")
|
216
|
+
@history.analyze_service.should == {:uptime => 10, :total_uptime => 30, :restarts => 1, :graceful_exits => 1,
|
217
|
+
:crashes => 1, :last_crash_time => 1000030}
|
218
|
+
@history.analyze_service.should == {:uptime => 20, :total_uptime => 40, :restarts => 1, :graceful_exits => 1,
|
219
|
+
:crashes => 1, :last_crash_time => 1000030}
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should re-analyze if there was a new event since last analysis" do
|
223
|
+
mock_time
|
224
|
+
@history.update("start")
|
225
|
+
@history.update("run")
|
226
|
+
@history.analyze_service.should == {:uptime => 10, :total_uptime => 10}
|
227
|
+
@history.instance_variable_set(:@pid, -1) # Simulate new process id following crash
|
228
|
+
@history.update("start")
|
229
|
+
@history.analyze_service.should == {:uptime => 0, :total_uptime => 20, :crashes => 1, :last_crash_time => 1000040}
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
data/spec/log_spec.rb
CHANGED
@@ -40,6 +40,21 @@ describe RightScale::Log do
|
|
40
40
|
|
41
41
|
before(:all) do
|
42
42
|
ENV['RS_LOG'] = 'true'
|
43
|
+
|
44
|
+
class RightScale::Platform::Filesystem
|
45
|
+
alias original_log_dir log_dir
|
46
|
+
def log_dir
|
47
|
+
# For specs, write all logs to temp_dir
|
48
|
+
# since log_dir may not be writable
|
49
|
+
temp_dir
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
after(:all) do
|
55
|
+
class RightScale::Platform::Filesystem
|
56
|
+
alias log_dir original_log_dir
|
57
|
+
end
|
43
58
|
end
|
44
59
|
|
45
60
|
before(:each) do
|
@@ -40,7 +40,7 @@ describe RightScale::OperationResult do
|
|
40
40
|
result = RightScale::OperationResult.from_results(@hash)
|
41
41
|
result.kind_of?(RightScale::OperationResult).should be_true
|
42
42
|
result.status_code.should == RightScale::OperationResult::SUCCESS
|
43
|
-
result.content.should ==
|
43
|
+
result.content.should == @hash.values[0].content
|
44
44
|
end
|
45
45
|
|
46
46
|
it "should handle individual result and return it as an OperationResult" do
|
@@ -158,7 +158,9 @@ describe RightScale::OperationResult do
|
|
158
158
|
|
159
159
|
it "should convert error content to string if necessary" do
|
160
160
|
result = RightScale::OperationResult.error({"data" => "some data", "message" => "some problem"})
|
161
|
-
result.to_s.should
|
161
|
+
result.to_s.should start_with ("error (")
|
162
|
+
result.to_s.should include("\"data\"=>\"some data\"")
|
163
|
+
result.to_s.should include("\"message\"=>\"some problem\"")
|
162
164
|
end
|
163
165
|
|
164
166
|
it "should truncate error message if too long" do
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe 'RightScale::Platform' do
|
4
|
+
subject { RightScale::Platform }
|
5
|
+
|
6
|
+
context 'under Darwin' do
|
7
|
+
context :installer do
|
8
|
+
context :install do
|
9
|
+
specify { lambda { subject.installer.install([]) }.should raise_exception }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end if RightScale::Platform.darwin?
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe RightScale::Platform do
|
4
|
+
subject { RightScale::Platform }
|
5
|
+
|
6
|
+
context 'under Linux' do
|
7
|
+
context :installer do
|
8
|
+
context :install do
|
9
|
+
it 'should succeed if no packages are specified' do
|
10
|
+
packages = []
|
11
|
+
flexmock(subject.installer).should_receive(:run_installer_command).and_return('ok')
|
12
|
+
subject.installer.install(packages).should == true
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should succeed if all packages install successfully' do
|
16
|
+
packages = ['syslog-ng']
|
17
|
+
flexmock(subject.installer).should_receive(:run_installer_command).and_return('ok')
|
18
|
+
subject.installer.install(packages).should == true
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should fail if one more packages are not found' do
|
22
|
+
if subject.installer.yum?
|
23
|
+
failure_message = "No package l33th4x0r available."
|
24
|
+
elsif subject.installer.aptitude?
|
25
|
+
failure_message = "E: Couldn't find package l33th4x0r"
|
26
|
+
elsif subject.installer.zypper?
|
27
|
+
failure_message = "Package 'l33h4x0r' not found."
|
28
|
+
end
|
29
|
+
packages = ['syslog-ng', 'l33th4x0r']
|
30
|
+
|
31
|
+
flexmock(subject.installer).should_receive(:run_installer_command).and_return(failure_message)
|
32
|
+
|
33
|
+
lambda { subject.installer.install(packages) }.should raise_error
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end if RightScale::Platform.linux?
|
@@ -21,64 +21,59 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
|
23
23
|
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
24
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'darwin')) if RightScale::Platform.darwin?
|
25
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'windows')) if RightScale::Platform.windows?
|
26
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'linux')) if RightScale::Platform.linux?
|
27
24
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
context :
|
33
|
-
|
34
|
-
|
35
|
-
subject.shell.uptime.should > 0
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'should be strictly increasing' do
|
39
|
-
u0 = subject.shell.uptime
|
40
|
-
sleep(1)
|
41
|
-
u1 = subject.shell.uptime
|
42
|
-
|
43
|
-
(u1 - u0).should >= 0
|
44
|
-
end
|
25
|
+
describe RightScale::Platform do
|
26
|
+
subject { RightScale::Platform }
|
27
|
+
|
28
|
+
context :shell do
|
29
|
+
context :uptime do
|
30
|
+
it 'should be positive' do
|
31
|
+
subject.shell.uptime.should > 0
|
45
32
|
end
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
b0 = subject.shell.booted_at
|
54
|
-
sleep(1)
|
55
|
-
b1 = subject.shell.booted_at
|
56
|
-
|
57
|
-
b0.should == b1
|
58
|
-
end
|
33
|
+
|
34
|
+
it 'should be strictly increasing' do
|
35
|
+
u0 = subject.shell.uptime
|
36
|
+
sleep(1)
|
37
|
+
u1 = subject.shell.uptime
|
38
|
+
|
39
|
+
(u1 - u0).should >= 0
|
59
40
|
end
|
60
41
|
end
|
61
42
|
|
62
|
-
context
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
@subject.instance_variable_set(:@eucalyptus, nil)
|
43
|
+
context :booted_at do
|
44
|
+
it 'should be some time in the past' do
|
45
|
+
Time.at(subject.shell.booted_at).to_i.should < Time.now.to_i
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should be constant' do
|
49
|
+
b0 = subject.shell.booted_at
|
50
|
+
sleep(1)
|
51
|
+
b1 = subject.shell.booted_at
|
52
|
+
|
53
|
+
b0.should == b1
|
74
54
|
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'cloud-family queries' do
|
59
|
+
# PLEASE NOTE: we are unable to use the magic RSpec "subject" because
|
60
|
+
# we are dispatching calls to a Singleton and we must partially mock its
|
61
|
+
# calls, and both __send__ and flexmock partial-mocking gets confused by
|
62
|
+
# the fact that #subject returns a Proc, not the actual subject. Do not
|
63
|
+
# attempt to use the pretty RSpec feature within this context. You have
|
64
|
+
# been warned!
|
65
|
+
before(:each) do
|
66
|
+
@subject = subject.instance
|
67
|
+
@subject.instance_variable_set(:@ec2, nil)
|
68
|
+
@subject.instance_variable_set(:@rackspace, nil)
|
69
|
+
@subject.instance_variable_set(:@eucalyptus, nil)
|
70
|
+
end
|
75
71
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
72
|
+
['ec2', 'rackspace', 'eucalyptus'].each do |cloud|
|
73
|
+
it "should detect #{cloud}" do
|
74
|
+
query = "#{cloud}?".to_sym
|
75
|
+
flexmock(@subject).should_receive(:read_cloud_file).once.and_return(cloud)
|
76
|
+
@subject.__send__(query).should be_true
|
82
77
|
end
|
83
78
|
end
|
84
79
|
end
|