right_agent 0.10.13 → 0.13.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/lib/right_agent.rb +2 -0
  2. data/lib/right_agent/actor.rb +45 -10
  3. data/lib/right_agent/actor_registry.rb +5 -5
  4. data/lib/right_agent/actors/agent_manager.rb +4 -4
  5. data/lib/right_agent/agent.rb +97 -37
  6. data/lib/right_agent/agent_tag_manager.rb +1 -2
  7. data/lib/right_agent/command/command_io.rb +1 -3
  8. data/lib/right_agent/command/command_runner.rb +9 -3
  9. data/lib/right_agent/dispatched_cache.rb +110 -0
  10. data/lib/right_agent/dispatcher.rb +119 -180
  11. data/lib/right_agent/history.rb +136 -0
  12. data/lib/right_agent/log.rb +6 -3
  13. data/lib/right_agent/monkey_patches/ruby_patch.rb +0 -1
  14. data/lib/right_agent/pid_file.rb +1 -1
  15. data/lib/right_agent/platform.rb +2 -2
  16. data/lib/right_agent/platform/linux.rb +8 -1
  17. data/lib/right_agent/platform/windows.rb +1 -1
  18. data/lib/right_agent/sender.rb +57 -41
  19. data/right_agent.gemspec +4 -4
  20. data/spec/actor_registry_spec.rb +7 -8
  21. data/spec/actor_spec.rb +87 -24
  22. data/spec/agent_spec.rb +107 -8
  23. data/spec/command/command_runner_spec.rb +12 -1
  24. data/spec/dispatched_cache_spec.rb +142 -0
  25. data/spec/dispatcher_spec.rb +110 -129
  26. data/spec/history_spec.rb +234 -0
  27. data/spec/idempotent_request_spec.rb +1 -1
  28. data/spec/log_spec.rb +15 -0
  29. data/spec/operation_result_spec.rb +4 -2
  30. data/spec/platform/darwin_spec.rb +13 -0
  31. data/spec/platform/linux_spec.rb +38 -0
  32. data/spec/platform/platform_spec.rb +46 -51
  33. data/spec/platform/windows_spec.rb +13 -0
  34. data/spec/sender_spec.rb +81 -38
  35. metadata +12 -9
  36. data/lib/right_agent/monkey_patches/ruby_patch/singleton_patch.rb +0 -45
  37. data/spec/platform/darwin.rb +0 -11
  38. data/spec/platform/linux.rb +0 -23
  39. 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
@@ -26,7 +26,7 @@ describe RightScale::IdempotentRequest do
26
26
 
27
27
  module RightScale
28
28
  class SenderMock
29
- include Singleton
29
+ include RightSupport::Ruby::EasySingleton
30
30
  end
31
31
  end
32
32
 
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 == 1
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 == "error ({\"data\"=>\"some data\", \"message\"=>\"some problem\"})"
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
- module RightScale
29
- describe Platform do
30
- subject { RightScale::Platform }
31
-
32
- context :shell do
33
- context :uptime do
34
- it 'should be positive' do
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
- context :booted_at do
48
- it 'should be some time in the past' do
49
- Time.at(subject.shell.booted_at).to_i.should < Time.now.to_i
50
- end
51
-
52
- it 'should be constant' do
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 'cloud-family queries' do
63
- # PLEASE NOTE: we are unable to use the magic RSpec "subject" because
64
- # we are dispatching calls to a Singleton and we must partially mock its
65
- # calls, and both __send__ and flexmock partial-mocking gets confused by
66
- # the fact that #subject returns a Proc, not the actual subject. Do not
67
- # attempt to use the pretty RSpec feature within this context. You have
68
- # been warned!
69
- before(:each) do
70
- @subject = subject.instance
71
- @subject.instance_variable_set(:@ec2, nil)
72
- @subject.instance_variable_set(:@rackspace, nil)
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
- ['ec2', 'rackspace', 'eucalyptus'].each do |cloud|
77
- it "should detect #{cloud}" do
78
- query = "#{cloud}?".to_sym
79
- flexmock(@subject).should_receive(:read_cloud_file).once.and_return(cloud)
80
- @subject.__send__(query).should be_true
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