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.
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