scout_agent 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/AUTHORS +4 -0
  2. data/CHANGELOG +3 -0
  3. data/COPYING +340 -0
  4. data/INSTALL +17 -0
  5. data/LICENSE +6 -0
  6. data/README +3 -0
  7. data/Rakefile +123 -0
  8. data/TODO +3 -0
  9. data/bin/scout_agent +11 -0
  10. data/lib/scout_agent.rb +73 -0
  11. data/lib/scout_agent/agent.rb +42 -0
  12. data/lib/scout_agent/agent/communication_agent.rb +85 -0
  13. data/lib/scout_agent/agent/master_agent.rb +301 -0
  14. data/lib/scout_agent/api.rb +241 -0
  15. data/lib/scout_agent/assignment.rb +105 -0
  16. data/lib/scout_agent/assignment/configuration.rb +30 -0
  17. data/lib/scout_agent/assignment/identify.rb +110 -0
  18. data/lib/scout_agent/assignment/queue.rb +95 -0
  19. data/lib/scout_agent/assignment/reset.rb +91 -0
  20. data/lib/scout_agent/assignment/snapshot.rb +92 -0
  21. data/lib/scout_agent/assignment/start.rb +149 -0
  22. data/lib/scout_agent/assignment/status.rb +44 -0
  23. data/lib/scout_agent/assignment/stop.rb +60 -0
  24. data/lib/scout_agent/assignment/upload_log.rb +61 -0
  25. data/lib/scout_agent/core_extensions.rb +260 -0
  26. data/lib/scout_agent/database.rb +386 -0
  27. data/lib/scout_agent/database/mission_log.rb +282 -0
  28. data/lib/scout_agent/database/queue.rb +126 -0
  29. data/lib/scout_agent/database/snapshots.rb +187 -0
  30. data/lib/scout_agent/database/statuses.rb +65 -0
  31. data/lib/scout_agent/dispatcher.rb +157 -0
  32. data/lib/scout_agent/id_card.rb +143 -0
  33. data/lib/scout_agent/lifeline.rb +243 -0
  34. data/lib/scout_agent/mission.rb +212 -0
  35. data/lib/scout_agent/order.rb +58 -0
  36. data/lib/scout_agent/order/check_in_order.rb +32 -0
  37. data/lib/scout_agent/order/snapshot_order.rb +33 -0
  38. data/lib/scout_agent/plan.rb +306 -0
  39. data/lib/scout_agent/server.rb +123 -0
  40. data/lib/scout_agent/tracked.rb +59 -0
  41. data/lib/scout_agent/wire_tap.rb +513 -0
  42. data/setup.rb +1360 -0
  43. data/test/tc_core_extensions.rb +89 -0
  44. data/test/tc_id_card.rb +115 -0
  45. data/test/tc_plan.rb +285 -0
  46. data/test/test_helper.rb +22 -0
  47. data/test/ts_all.rb +7 -0
  48. metadata +171 -0
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env ruby -wKU
2
+
3
+ require "test/unit"
4
+
5
+ require "scout_agent/core_extensions"
6
+
7
+ class TestCoreExtensions < Test::Unit::TestCase
8
+ def test_to_word_list
9
+ { %w[] => "",
10
+ %w[one] => "one",
11
+ %w[one two] => "one and two",
12
+ %w[one two three] => "one, two, and three",
13
+ %w[one two three four] => "one, two, three, and four" }.each do |arr, str|
14
+ assert_equal(str, arr.to_word_list)
15
+ end
16
+ end
17
+
18
+ def test_to_word_list_with_custom_conjunction
19
+ assert_equal("one, two, or three", %w[one two three].to_word_list("or"))
20
+ end
21
+
22
+ def test_string_camel_case
23
+ { "class_name" => "ClassName",
24
+ "symbols::are/removed" => "SymbolsAreRemoved",
25
+ "spaces are removed" => "SpacesAreRemoved",
26
+ "agent_99_smart" => "Agent99Smart",
27
+ "NoChange" => "NoChange" }.each do |snake_case, camel_case|
28
+ assert_equal(camel_case, snake_case.CamelCase)
29
+ end
30
+ end
31
+
32
+ def test_string_camel_case_alias
33
+ str = "test_string"
34
+ assert_equal(str.CamelCase, str.camel_case)
35
+ end
36
+
37
+ def test_string_snake_case
38
+ { "ClassName" => "class_name",
39
+ "Symbols::Are/Unified" => "symbols_are_unified",
40
+ "Spaces Are Converted" => "spaces_are_converted",
41
+ "Agent99Smart" => "agent_99_smart",
42
+ "no_change" => "no_change" }.each do |camel_case, snake_case|
43
+ assert_equal(snake_case, camel_case.snake_case)
44
+ end
45
+ end
46
+
47
+ def test_string_trim_with_default
48
+ heredoc = <<-END_DEFAULT.trim
49
+ I am indented on purpose.
50
+ Leading space should be removed.
51
+
52
+ but not these two spaces
53
+ END_DEFAULT
54
+ assert_match(/^I/, heredoc)
55
+ assert_match(/^Leading/, heredoc)
56
+ assert_match(/^ but/, heredoc)
57
+ end
58
+
59
+ def test_string_trim_with_width
60
+ heredoc = <<-END_WIDTH.trim(2)
61
+ I'm indented four,
62
+ but we will just trim two.
63
+ END_WIDTH
64
+ assert_match(/^ I'm/, heredoc)
65
+ assert_match(/^ but/, heredoc)
66
+ end
67
+
68
+ def test_to_question
69
+ assert_match(/\? \z/, "A simple question?\n\t ".to_question)
70
+ end
71
+
72
+ def test_word_wrap_with_default
73
+ assert_no_match(/^.{61}/, ("A short and simple string." * 1000).word_wrap)
74
+ end
75
+
76
+ def test_word_wrap_with_width
77
+ assert_no_match( /^.{11}/,
78
+ ("A short and simple string." * 1000).word_wrap(10) )
79
+ end
80
+
81
+ def test_word_wrap_skips_unbroken_content
82
+ assert_equal("0123456789", "0123456789".word_wrap(4))
83
+ end
84
+
85
+ def test_word_wrap_strips_and_simplifes_space
86
+ assert_equal( "one two\nthree",
87
+ " \t\none\n \ttwo three\n\n \t".word_wrap(10) )
88
+ end
89
+ end
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env ruby -wKU
2
+
3
+ require "test_helper"
4
+
5
+ require "scout_agent"
6
+
7
+ class TestIDCard < Test::Unit::TestCase
8
+ def setup
9
+ plan.prefix_path = test_prefix
10
+ plan.build_pid_dir(Process.egid)
11
+ end
12
+
13
+ def teardown
14
+ id_card(:test).revoke
15
+ test_prefix.rmtree if test_prefix.exist?
16
+ plan.reset_defaults
17
+ end
18
+
19
+ def test_authorize_creates_pid_file
20
+ @card = id_card(:test)
21
+ assert(!@card.pid_file.exist?, "PID file already existed")
22
+ assert(@card.authorize, "Failed to authorize our PID")
23
+ assert(@card.pid_file.exist?, "PID wasn't created")
24
+ assert_equal("#{Process.pid}\n", @card.pid_file.read)
25
+ end
26
+
27
+ def test_authorize_fails_for_an_existing_pid
28
+ test_authorize_creates_pid_file # make sure we already have a file
29
+ assert(!@card.authorize, "Authorized with an existing PID file")
30
+ end
31
+
32
+ def test_authorize_will_clear_a_stale_pid_file
33
+ create_and_kill_another_process
34
+ card = id_card(:test)
35
+ assert_not_equal(Process.pid, card.pid_file.read)
36
+ assert(card.authorize, "Failed to replace stale PID file")
37
+ assert_equal("#{Process.pid}\n", card.pid_file.read)
38
+ end
39
+
40
+ def test_successful_authorize_updates_me
41
+ ScoutAgent::IDCard.me = nil
42
+ test_authorize_creates_pid_file
43
+ assert_equal(@card, ScoutAgent::IDCard.me)
44
+ end
45
+
46
+ def test_revoke_clears_a_pid_file
47
+ test_authorize_creates_pid_file # create a file
48
+ assert(@card.revoke, "Failed to clear file")
49
+ end
50
+
51
+ def test_revoke_return_true_if_cleared_false_otherwise
52
+ test_authorize_creates_pid_file # create a file
53
+ assert(@card.revoke, "Failed to clear file")
54
+ assert(!@card.revoke, "Clear a PID file that was already cleared")
55
+ end
56
+
57
+ def test_pid_file_is_named_file
58
+ card = id_card(:my_name)
59
+ assert_match(/my_name\.pid\z/, card.pid_file.to_s)
60
+ end
61
+
62
+ def test_pid_is_a_shorcut_for_reading_pid_file
63
+ test_authorize_creates_pid_file # create a file
64
+ assert_equal(@card.pid_file.read.to_i, @card.pid)
65
+ end
66
+
67
+ def test_pid_returns_nil_for_a_missing_pid_file
68
+ assert_nil(id_card(:does_not_exist).pid)
69
+ end
70
+
71
+ def test_signal_delivers_messages
72
+ received_message = false
73
+ trap("USR1") { received_message = true }
74
+ test_authorize_creates_pid_file
75
+
76
+ # have another process send us a signal
77
+ other_pid = fork { @card.signal("USR1") }
78
+ Process.wait(other_pid)
79
+
80
+ assert(received_message, "Didn't receive signal")
81
+ end
82
+
83
+ def test_signal_returns_false_for_missing_pid_files
84
+ assert(!id_card(:does_not_exist).signal(0), "Signal was sent without a PID")
85
+ end
86
+
87
+ def test_signal_errors_bubble_up_to_caller
88
+ create_and_kill_another_process
89
+ card = id_card(:test)
90
+ assert_raise(Errno::ESRCH) { card.signal("KILL") }
91
+ end
92
+
93
+ def test_to_s_includes_process_name_and_pid
94
+ test_authorize_creates_pid_file # make an identity
95
+ assert_equal("test (#{Process.pid})", @card.to_s)
96
+ end
97
+
98
+ def test_to_s_uses_unauthorized_for_a_missing_pid
99
+ assert_equal("missing (unauthorized)", id_card(:missing).to_s)
100
+ end
101
+
102
+ private
103
+
104
+ def id_card(*args)
105
+ ScoutAgent::IDCard.new(*args)
106
+ end
107
+
108
+ def create_and_kill_another_process
109
+ other_pid = fork do
110
+ test_authorize_creates_pid_file # make sure we claim the PID file
111
+ exit! # and exist without clearing it
112
+ end
113
+ Process.wait(other_pid)
114
+ end
115
+ end
data/test/tc_plan.rb ADDED
@@ -0,0 +1,285 @@
1
+ #!/usr/bin/env ruby -wKU
2
+
3
+ require "test_helper"
4
+ require "tempfile"
5
+
6
+ require "scout_agent"
7
+
8
+ class TestPlan < Test::Unit::TestCase
9
+ def teardown
10
+ test_prefix.rmtree if test_prefix.exist?
11
+ end
12
+
13
+ ############
14
+ ### Type ###
15
+ ############
16
+
17
+ def test_plan_is_a_customized_ostruct
18
+ assert_instance_of(OpenStruct, plan)
19
+ end
20
+
21
+ ########################
22
+ ### Loading a Config ###
23
+ ########################
24
+
25
+ def test_updating_plan_from_a_config_file
26
+ c = load_config_file(<<-END_SETTINGS)
27
+ config.string_setting = "just a String"
28
+ config.integer_setting = 42
29
+ END_SETTINGS
30
+ assert_equal(plan, c)
31
+ assert_equal("just a String", c.string_setting)
32
+ assert_equal(42, c.integer_setting)
33
+ end
34
+
35
+ def test_exceptions_bubble_up_to_the_caller_from_config_file
36
+ assert_raise(RuntimeError) do
37
+ load_config_file('raise "Oops!"')
38
+ end
39
+ end
40
+
41
+ def test_updating_plan_from_switches
42
+ assert_nil(plan.switch_one)
43
+ assert_nil(plan.switch_two)
44
+ plan.update_from_switches(:switch_one => "One", :switch_two => "Two")
45
+ assert_equal("One", plan.switch_one)
46
+ assert_equal("Two", plan.switch_two)
47
+ end
48
+
49
+ ################
50
+ ### Defaults ###
51
+ ################
52
+
53
+ def test_defaults_are_available_in_an_enumerable_list
54
+ assert_kind_of(Enumerable, plan.defaults)
55
+ end
56
+
57
+ # def test_agent_name_is_set
58
+ # assert_match(/\A\w+\z/, plan.agent_name)
59
+ # assert_equal( plan.agent_name.split("_"),
60
+ # plan.proper_agent_name.downcase.split(" ") )
61
+ # end
62
+ #
63
+ # def test_agent_namespace_returns_module_based_on_agent_name
64
+ # assert_equal(ScoutAgent, plan.agent_namespace)
65
+ # end
66
+
67
+ def test_all_paths_return_pathname_objects
68
+ %w[ prefix_path
69
+ os_config_path
70
+ os_db_path
71
+ os_pid_path
72
+ os_log_path
73
+ config_file
74
+ db_dir
75
+ pid_dir
76
+ log_dir ].each do |path|
77
+ assert_instance_of(Pathname, plan(path))
78
+ end
79
+ end
80
+
81
+ def test_prefix_path_defaults_to_the_root_path
82
+ assert_equal(Pathname.new("/"), plan.prefix_path)
83
+ end
84
+
85
+ def test_default_os_paths_are_set
86
+ %w[ os_config_path
87
+ os_db_path
88
+ os_pid_path
89
+ os_log_path ].each do |path|
90
+ assert_path_match(%r{\A(?:/\w+)+/?\z}, path)
91
+ end
92
+ end
93
+
94
+ def test_default_paths_are_set
95
+ assert_path_match(%r{\A(?:/\w+)+\.\w+\z}, :config_file)
96
+ %w[db_dir pid_dir log_dir].each do |dir|
97
+ assert_path_match(%r{\A(?:/\w+)+/?\z}, dir)
98
+ end
99
+ end
100
+
101
+ def test_server_url_is_set
102
+ assert_match(%r{\Ahttps://}, plan.server_url)
103
+ end
104
+
105
+ def test_run_as_daemon_is_set
106
+ assert(plan.run_as_daemon, "Daemon mode should default to on")
107
+ end
108
+
109
+ def test_run_as_daemon_alias
110
+ assert_equal(plan.run_as_daemon, plan.run_as_daemon?)
111
+ end
112
+
113
+ def test_user_choices_is_set
114
+ assert_equal(%w[daemon nobody], plan.user_choices)
115
+ end
116
+
117
+ def test_group_choices_is_set
118
+ assert_equal(%w[daemon nogroup], plan.group_choices)
119
+ end
120
+
121
+ def test_reset_defaults
122
+ current = plan.defaults.map { |name, _| [name, plan(name)] }
123
+ plan.defaults.each { |name, _| plan("#{name}=", "Junk!") }
124
+ current.each { |name, value| assert_not_equal(value, plan(name)) }
125
+
126
+ plan.reset_defaults
127
+ current.each { |name, value| assert_equal(value, plan(name)) }
128
+ end
129
+
130
+ ####################
131
+ ### Nested Paths ###
132
+ ####################
133
+
134
+ def test_prefix_path_prepends_to_all_paths
135
+ %w[ os_config_path
136
+ os_db_path
137
+ os_pid_path
138
+ os_log_path
139
+ config_file
140
+ db_dir
141
+ pid_dir
142
+ log_dir ].each do |path|
143
+ configure(:prefix_path => test_prefix) do
144
+ assert_path_match(%r{\A#{test_prefix}/.+\z}, path)
145
+ end
146
+ end
147
+ end
148
+
149
+ def test_os_paths_prepends_to_full_paths
150
+ { :config_file => :os_config_path,
151
+ :db_dir => :os_db_path,
152
+ :pid_dir => :os_pid_path,
153
+ :log_dir => :os_log_path }.each do |path, parent|
154
+ assert_prepends_path(path, parent)
155
+ end
156
+ end
157
+
158
+ def test_prefix_path_nests_with_os_paths
159
+ dir = "test_dir"
160
+ { :config_file => :os_config_path,
161
+ :db_dir => :os_db_path,
162
+ :pid_dir => :os_pid_path,
163
+ :log_dir => :os_log_path }.each do |path, parent|
164
+ configure(:prefix_path => test_prefix, parent => dir) do
165
+ assert_path_match(%r{\A#{test_prefix}/#{dir}/.+\z}, path)
166
+ end
167
+ end
168
+ end
169
+
170
+ ################
171
+ ### Creation ###
172
+ ################
173
+
174
+ def test_write_default_config_file_creates_a_readable_ruby_configuration_file
175
+ configure(:prefix_path => test_prefix) do
176
+ assert(!File.exist?(plan.config_file), "Configuration already existed")
177
+ assert(plan.write_default_config_file, "Could not create config file")
178
+ assert(File.exist?(plan.config_file), "Configuration not created")
179
+ assert_equal("755", plan.config_file.stat.mode.to_s(8)[-3..-1])
180
+ end
181
+ end
182
+
183
+ def test_write_default_config_file_wont_replace_an_existing_file
184
+ configure(:prefix_path => test_prefix) do
185
+ assert(plan.write_default_config_file, "Could not create config file")
186
+ assert(!plan.write_default_config_file, "Replaced config file")
187
+ end
188
+ end
189
+
190
+ def test_builders_fails_if_they_dont_have_permission
191
+ configure(:prefix_path => test_prefix) do
192
+ { :write_default_config_file => :os_config_path,
193
+ :build_db_dir => :os_db_path,
194
+ :build_pid_dir => :os_pid_path,
195
+ :build_log_dir => :os_log_path }.each do |builder, dir|
196
+ plan(dir).mkpath
197
+ plan(dir).chmod(0444) # read only
198
+ args = builder == :write_default_config_file ? [ ] : [Process.egid]
199
+ assert(!plan(builder, *args), "Built without permission")
200
+ end
201
+ end
202
+ end
203
+
204
+ # def test_directory_builders_create_a_readable_and_writable_directory
205
+ # configure(:prefix_path => test_prefix) do
206
+ # %w[db_dir pid_dir log_dir].each do |dir|
207
+ # assert(!File.exist?(plan(dir)), "Directory already existed")
208
+ # assert(plan("build_#{dir}", Process.egid), "Could not create directory")
209
+ # assert(File.exist?(plan(dir)), "Directory not created")
210
+ # assert_equal("775", plan(dir).stat.mode.to_s(8)[-3..-1])
211
+ # end
212
+ # end
213
+ # end
214
+
215
+ ###################
216
+ ### Validations ###
217
+ ###################
218
+
219
+ # def test_plan_is_present
220
+ # configure(:prefix_path => test_prefix) do
221
+ # assert(!plan.present?, "Card was present when missing")
222
+ # # add some config
223
+ # %w[db_dir log_dir].each do |dir|
224
+ # assert(plan("build_#{dir}", Process.egid), "Could not create directory")
225
+ # end
226
+ # assert(!plan.present?, "Card was present when partially build")
227
+ # # complete config
228
+ # assert(plan.write_default_config_file, "Could not create config file")
229
+ # assert(plan.present?, "Complete card was not present")
230
+ # end
231
+ # end
232
+ #
233
+ # def test_plan_is_valid
234
+ # configure(:prefix_path => test_prefix) do
235
+ # assert(!plan.valid?, "Card was valid when missing")
236
+ # # add config
237
+ # %w[db_dir log_dir].each do |dir|
238
+ # assert(plan("build_#{dir}", Process.egid), "Could not create directory")
239
+ # end
240
+ # assert(plan.write_default_config_file, "Could not create config file")
241
+ # assert(plan.valid?, "Complete card was not valid")
242
+ # # break premissions
243
+ # plan.db_dir.chmod(0444) # read only
244
+ # assert(!plan.valid?, "Card was valid when read only")
245
+ # end
246
+ # end
247
+
248
+ #######
249
+ private
250
+ #######
251
+
252
+ # Creates a Tempfile, dumps configuration to it, and loads it.
253
+ def load_config_file(content = String.new)
254
+ cf = Tempfile.new("agent_config_test")
255
+ cf << content
256
+ cf.flush
257
+ plan.update_from_config_file(cf.path)
258
+ end
259
+
260
+ #
261
+ # Applies +settings+ to the configuaration, runs the passed block, then resets
262
+ # configuaration defaults.
263
+ #
264
+ def configure(settings)
265
+ settings.each { |name, value| plan("#{name}=", value) }
266
+ begin
267
+ yield
268
+ ensure
269
+ plan.reset_defaults
270
+ end
271
+ end
272
+
273
+ # Checks a Pathname +path+ against a +regexp+.
274
+ def assert_path_match(regexp, path)
275
+ assert_match(regexp, plan(path).to_s)
276
+ end
277
+
278
+ # Sets the +parent+ dir and ensures that +path+ is updated as a result.
279
+ def assert_prepends_path(path, parent)
280
+ dir = "test_dir"
281
+ configure(parent => dir) do
282
+ assert_path_match(%r{\A/#{dir}/.+\z}, path)
283
+ end
284
+ end
285
+ end