aws-codedeploy-agent 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/.gitignore +2 -0
  2. data/CHANGES.md +3 -0
  3. data/Gemfile +13 -0
  4. data/LICENSE +177 -0
  5. data/NOTICE +2 -0
  6. data/README.md +16 -0
  7. data/aws-codedeploy-agent.gemspec +39 -0
  8. data/bin/codedeploy-agent +78 -0
  9. data/bin/codedeploy-install +15 -0
  10. data/bin/codedeploy-uninstall +13 -0
  11. data/certs/host-agent-deployment-signer-ca-chain.pem +76 -0
  12. data/conf/codedeployagent.yml +9 -0
  13. data/init.d/codedeploy-agent +61 -0
  14. data/lib/core_ext.rb +71 -0
  15. data/lib/instance_agent.rb +35 -0
  16. data/lib/instance_agent/agent/base.rb +34 -0
  17. data/lib/instance_agent/codedeploy_plugin/application_specification/ace_info.rb +133 -0
  18. data/lib/instance_agent/codedeploy_plugin/application_specification/acl_info.rb +163 -0
  19. data/lib/instance_agent/codedeploy_plugin/application_specification/application_specification.rb +142 -0
  20. data/lib/instance_agent/codedeploy_plugin/application_specification/context_info.rb +23 -0
  21. data/lib/instance_agent/codedeploy_plugin/application_specification/file_info.rb +23 -0
  22. data/lib/instance_agent/codedeploy_plugin/application_specification/linux_permission_info.rb +121 -0
  23. data/lib/instance_agent/codedeploy_plugin/application_specification/mode_info.rb +66 -0
  24. data/lib/instance_agent/codedeploy_plugin/application_specification/range_info.rb +134 -0
  25. data/lib/instance_agent/codedeploy_plugin/application_specification/script_info.rb +27 -0
  26. data/lib/instance_agent/codedeploy_plugin/codedeploy_control.rb +72 -0
  27. data/lib/instance_agent/codedeploy_plugin/command_executor.rb +357 -0
  28. data/lib/instance_agent/codedeploy_plugin/command_poller.rb +146 -0
  29. data/lib/instance_agent/codedeploy_plugin/deployment_specification.rb +150 -0
  30. data/lib/instance_agent/codedeploy_plugin/hook_executor.rb +206 -0
  31. data/lib/instance_agent/codedeploy_plugin/install_instruction.rb +374 -0
  32. data/lib/instance_agent/codedeploy_plugin/installer.rb +143 -0
  33. data/lib/instance_agent/codedeploy_plugin/request_helper.rb +28 -0
  34. data/lib/instance_agent/config.rb +43 -0
  35. data/lib/instance_agent/log.rb +3 -0
  36. data/lib/instance_agent/platform.rb +17 -0
  37. data/lib/instance_agent/platform/linux_util.rb +57 -0
  38. data/lib/instance_agent/runner/child.rb +57 -0
  39. data/lib/instance_agent/runner/master.rb +103 -0
  40. data/lib/instance_metadata.rb +47 -0
  41. data/test/certificate_helper.rb +120 -0
  42. data/test/helpers/instance_agent_helper.rb +25 -0
  43. data/test/instance_agent/agent/base_test.rb +49 -0
  44. data/test/instance_agent/codedeploy_plugin/application_specification_test.rb +1710 -0
  45. data/test/instance_agent/codedeploy_plugin/codedeploy_control_test.rb +51 -0
  46. data/test/instance_agent/codedeploy_plugin/command_executor_test.rb +513 -0
  47. data/test/instance_agent/codedeploy_plugin/command_poller_test.rb +459 -0
  48. data/test/instance_agent/codedeploy_plugin/deployment_specification_test.rb +335 -0
  49. data/test/instance_agent/codedeploy_plugin/hook_executor_test.rb +250 -0
  50. data/test/instance_agent/codedeploy_plugin/install_instruction_test.rb +566 -0
  51. data/test/instance_agent/codedeploy_plugin/installer_test.rb +519 -0
  52. data/test/instance_agent/codedeploy_plugin/request_helper_test.rb +37 -0
  53. data/test/instance_agent/config_test.rb +64 -0
  54. data/test/instance_agent/runner/child_test.rb +87 -0
  55. data/test/instance_metadata_test.rb +97 -0
  56. data/test/test_helper.rb +16 -0
  57. data/vendor/gems/.codedeploy-commands-1.0.0.created.rid +1 -0
  58. data/vendor/gems/codedeploy-commands/apis/CodeDeployCommand.api.json +372 -0
  59. data/vendor/gems/codedeploy-commands/codedeploy-commands-1.0.0.gemspec +28 -0
  60. data/vendor/gems/codedeploy-commands/lib/aws/codedeploy_commands.rb +18 -0
  61. data/vendor/gems/codedeploy-commands/lib/aws/plugins/certificate_authority.rb +12 -0
  62. data/vendor/gems/codedeploy-commands/lib/aws/plugins/deploy_control_endpoint.rb +22 -0
  63. data/vendor/gems/process_manager/README.md +1 -0
  64. data/vendor/gems/process_manager/lib/blank.rb +153 -0
  65. data/vendor/gems/process_manager/lib/core_ext.rb +73 -0
  66. data/vendor/gems/process_manager/lib/process_manager.rb +49 -0
  67. data/vendor/gems/process_manager/lib/process_manager/child.rb +119 -0
  68. data/vendor/gems/process_manager/lib/process_manager/config.rb +112 -0
  69. data/vendor/gems/process_manager/lib/process_manager/log.rb +107 -0
  70. data/vendor/gems/process_manager/lib/process_manager/master.rb +322 -0
  71. data/vendor/gems/process_manager/process_manager-0.0.13.gemspec +42 -0
  72. data/vendor/specifications/aws-sdk-core-2.0.5.gemspec +39 -0
  73. data/vendor/specifications/builder-3.2.2.gemspec +29 -0
  74. data/vendor/specifications/codedeploy-commands-1.0.0.gemspec +28 -0
  75. data/vendor/specifications/gli-2.5.6.gemspec +51 -0
  76. data/vendor/specifications/jamespath-0.5.1.gemspec +35 -0
  77. data/vendor/specifications/little-plugger-1.1.3.gemspec +32 -0
  78. data/vendor/specifications/logging-1.8.1.gemspec +44 -0
  79. data/vendor/specifications/multi_json-1.7.7.gemspec +30 -0
  80. data/vendor/specifications/multi_json-1.8.4.gemspec +30 -0
  81. data/vendor/specifications/multi_xml-0.5.5.gemspec +30 -0
  82. data/vendor/specifications/process_manager-0.0.13.gemspec +42 -0
  83. data/vendor/specifications/simple_pid-0.2.1.gemspec +28 -0
  84. metadata +377 -0
@@ -0,0 +1,112 @@
1
+ # encoding: UTF-8
2
+ module ProcessManager
3
+ class Config
4
+
5
+ def self.init
6
+ @config = Config.new
7
+ end
8
+
9
+ def self.config(options = {})
10
+ init unless @config
11
+ @config.config(options)
12
+ end
13
+
14
+ def self.validate_config
15
+ init unless @config
16
+ @config.validate
17
+ end
18
+
19
+ def self.load_config
20
+ if File.exists?(config[:config_file]) && File.readable?(config[:config_file])
21
+ file_config = YAML.load(File.read(config[:config_file])).symbolize_keys
22
+ config.update(file_config)
23
+ config_loaded_callbacks.each{|c| c.call}
24
+ else
25
+ raise "The config file #{config[:config_file]} does not exist or is not readable"
26
+ end
27
+ end
28
+
29
+ def self.on_config_load(&block)
30
+ @@_config_callbacks ||= []
31
+ @@_config_callbacks << block
32
+ nil
33
+ end
34
+
35
+ def self.config_loaded_callbacks
36
+ @@_config_callbacks ||= []
37
+ end
38
+
39
+ def initialize
40
+ @config = {
41
+ :program_name => 'process_manager',
42
+ :max_runs_per_worker => 0, # unlimited
43
+ :children => 4,
44
+ :log_dir => '/tmp',
45
+ :pid_dir => '/tmp',
46
+ :verbose => false,
47
+ :wait_after_throttle_error => 60, # wait time in seconds after a we got a throttling exception from SWF
48
+ :wait_between_runs => 5, # wait time in seconds after a run so that we don't run into throttling exceptions
49
+ :wait_after_connection_problem => 5, # wait time in seconds after a connection problem as we don't want to build a fork-bomb
50
+ :wait_between_spawning_children => 10, # wait time in seconds after spawning a child so that we don't overhelm SWF with our requests
51
+ :user => nil,
52
+ :group => nil,
53
+
54
+ # global config file to read
55
+ :config_file => nil
56
+ }
57
+ end
58
+
59
+ def config(options = {})
60
+ @config.update(options) unless options.nil? || options.empty?
61
+ @config
62
+ end
63
+
64
+ def validate
65
+ errors = []
66
+
67
+ errors << "Invalid max_runs_per_worker #{config[:max_runs_per_worker].inspect}" unless config[:max_runs_per_worker].to_s.match(/\d+/) && config[:max_runs_per_worker].to_i >= 0
68
+ config[:max_runs_per_worker] = config[:max_runs_per_worker].to_i
69
+ errors << "Invalid number of children #{config[:children].inspect}" unless config[:children].to_s.match(/\d+/) && config[:children].to_i > 0
70
+ config[:children] = config[:children].to_i
71
+
72
+ normalize_log_and_pid_dir
73
+ validate_log_and_pid_dir(errors)
74
+ validate_user(errors)
75
+ errors
76
+ end
77
+
78
+ def validate_log_and_pid_dir(errors)
79
+ FileUtils.mkdir_p(ProcessManager::Config.config[:log_dir]) unless File.exists?(ProcessManager::Config.config[:log_dir])
80
+ FileUtils.mkdir_p(ProcessManager::Config.config[:pid_dir]) unless File.exists?(ProcessManager::Config.config[:pid_dir])
81
+ errors << "Please make sure the path of the log directory exists and is writable: #{config[:log_dir].inspect}" unless file_writable?(config[:log_dir]) && File.directory?(config[:log_dir])
82
+ errors << "Please make sure the path of the PID directory exists and is writable: #{config[:pid_dir].inspect}" unless file_writable?(config[:pid_dir]) && File.directory?(config[:pid_dir])
83
+ errors
84
+ end
85
+
86
+ def validate_user(errors)
87
+ if config[:user].present?
88
+ errors << "The system user does not exist: #{config[:user].inspect}" unless (Etc.getpwnam(config[:user]).uid rescue false)
89
+ end
90
+ errors
91
+ end
92
+
93
+ def normalize_log_and_pid_dir
94
+ if config[:pid_dir]
95
+ config[:pid_dir] = File.expand_path(config[:pid_dir])
96
+ end
97
+ if config[:log_dir]
98
+ config[:log_dir] = File.expand_path(config[:log_dir])
99
+ end
100
+ end
101
+
102
+ def file_writable?(path)
103
+ return false unless path.present?
104
+ if File.exists?(path)
105
+ File.writable?(path)
106
+ else
107
+ File.writable?(File.dirname(path))
108
+ end
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,107 @@
1
+ # encoding: UTF-8
2
+ require 'logging'
3
+ require 'logger'
4
+
5
+ module ProcessManager
6
+ class Log
7
+ NORMAL_SEVERITIES = %W{debug info warn unknown}
8
+ ERROR_SEVERITIES = %W{error fatal}
9
+ SEVERITIES = NORMAL_SEVERITIES + ERROR_SEVERITIES
10
+
11
+ class << self
12
+ attr_accessor :logger
13
+ end
14
+
15
+ class Logger
16
+ attr_accessor :logger
17
+
18
+ # Initializes a logger that will roll log files every hour and keeps a week's worth of logs
19
+ # in the disk. The log layout represents,
20
+ # "ISO8601_format_date log_level [program_name(pid)]: actual_log_message"
21
+ # Note: Rolling file appender only works with regular files
22
+
23
+ def initialize(log_device)
24
+ @logger = Logging.logger[ProcessManager::Config.config[:program_name]]
25
+ @logger.add_appenders(
26
+ Logging.appenders.rolling_file('rolling_file_appender',
27
+ :filename => log_device,
28
+ :age => 'daily',
29
+ :keep => 7,
30
+ :layout => Logging.layouts.pattern(:pattern => '%d %-5l [%c(%p)]: %m\n')
31
+ )
32
+ )
33
+ if ProcessManager::Config.config[:verbose]
34
+ self.level = 'debug'
35
+ else
36
+ self.level = 'info'
37
+ end
38
+ end
39
+
40
+ def level=(level)
41
+ if level.is_a?(Fixnum)
42
+ @logger.level = level
43
+ else
44
+ @logger.level = ::Logger.const_get(level.to_s.upcase)
45
+ end
46
+ end
47
+
48
+ NORMAL_SEVERITIES.each do |level|
49
+ log_level = ::Logger.const_get(level.upcase)
50
+ define_method(level) do |message|
51
+ raise "No logger available" unless @logger
52
+ @logger.add(log_level, message)
53
+ end
54
+ end
55
+
56
+ ERROR_SEVERITIES.each do |level|
57
+ log_level = ::Logger.const_get(level.upcase)
58
+ define_method(level) do |message|
59
+ @logger.add(log_level, message)
60
+ ProcessManager.on_error_callbacks.each do |callback|
61
+ begin
62
+ callback.call(message)
63
+ rescue Exception
64
+ # error callbacks shouldn't break the main flow
65
+ end
66
+ end if level == 'error'
67
+ end
68
+ end
69
+ end
70
+
71
+ def self.[](logger_name)
72
+ logger_name = logger_name.to_sym
73
+ @logger_collection ||= {}
74
+ @logger_collection[logger_name] ||= ProcessManager::Log::Logger.new(log_device(logger_name))
75
+ end
76
+
77
+ def self.log_device(logger_name)
78
+ if logger_name.is_a?(String) || logger_name.is_a?(Symbol)
79
+ raise 'Please init ProcessManager::Log with a base log file!' unless @base_log_file
80
+ @base_log_file.gsub(/\.log/, ".#{logger_name.to_s.demodulize}.log")
81
+ else # IO?
82
+ logger_name
83
+ end
84
+ end
85
+
86
+ def self.init(log_device)
87
+ @base_log_file = log_device
88
+ if @logger.nil? || ((@logger.logger.logdev.dev.path != log_device) rescue true)
89
+ @logger = ::ProcessManager::Log::Logger.new(log_device)
90
+ end
91
+ @logger
92
+ end
93
+
94
+ def self.level=(level)
95
+ @logger.level = ::Logger.const_get(level.to_s.upcase)
96
+ end
97
+
98
+ (NORMAL_SEVERITIES + ERROR_SEVERITIES).each do |level|
99
+ singleton_class.instance_eval do
100
+ define_method(level) do |message|
101
+ raise "No logger available" unless @logger
102
+ @logger.send(level, message)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,322 @@
1
+ # encoding: UTF-8
2
+ require 'simple_pid'
3
+ require 'fileutils'
4
+ require 'blank'
5
+
6
+ module ProcessManager
7
+ module Daemon
8
+ class Master
9
+
10
+ attr_accessor :children
11
+
12
+ def initialize
13
+ @children = {}
14
+ ProcessManager.set_program_name(description)
15
+ ensure_validate_configuration
16
+ dropped_privileges = drop_privileges
17
+ ProcessManager::Log.init(log_file)
18
+ if dropped_privileges
19
+ ProcessManager::Log.info("Dropped privileges to group: #{Etc.getgrgid(Process.gid).name} (gid = #{Process.gid})")
20
+ ProcessManager::Log.info("Dropped privileges to user: #{Etc.getpwuid(Process.uid).name} (uid = #{Process.uid})")
21
+ end
22
+ after_initialize
23
+ end
24
+
25
+ # please override
26
+ def after_initialize
27
+ # hook
28
+ end
29
+
30
+ # please override
31
+ def validate_ssl_config
32
+ end
33
+
34
+ def self.start
35
+ pid = fork do
36
+ new.start
37
+ end
38
+ Process.detach pid
39
+ end
40
+
41
+ def self.stop
42
+ new.stop
43
+ end
44
+
45
+ def self.restart
46
+ stop
47
+ sleep 1
48
+ start
49
+ end
50
+
51
+ def self.status
52
+ if pid = find_pid
53
+ if ProcessManager::process_running?(pid)
54
+ pid
55
+ else
56
+ clean_stale_pid
57
+ nil
58
+ end
59
+ else
60
+ # does not run
61
+ nil
62
+ end
63
+ end
64
+
65
+ def self.pid_file
66
+ File.join(ProcessManager::Config.config[:pid_dir], "#{ProcessManager::Config.config[:program_name]}.#{self.pid_description}.pid")
67
+ end
68
+
69
+ def self.pid_lock_file
70
+ File.join(ProcessManager::Config.config[:pid_dir], "#{ProcessManager::Config.config[:program_name]}.pid.lock")
71
+ end
72
+
73
+ def pid_lock_file
74
+ self.class.pid_lock_file
75
+ end
76
+
77
+ def pid_file
78
+ self.class.pid_file
79
+ end
80
+
81
+ # please override
82
+ def self.pid_description
83
+ "ProcessManager"
84
+ end
85
+
86
+ def pid_description
87
+ self.class.pid_description
88
+ end
89
+
90
+ def self.log_file
91
+ File.join(ProcessManager::Config.config[:log_dir], "#{ProcessManager::Config.config[:program_name]}.#{pid_description}.log")
92
+ end
93
+
94
+ def log_file
95
+ self.class.log_file
96
+ end
97
+
98
+ def drop_privileges
99
+
100
+ runas_user = ProcessManager::Config.config[:user]
101
+ return false if runas_user.blank?
102
+
103
+ if runas_user == Etc.getpwuid(Process.uid).name
104
+ return false
105
+ elsif Process.uid != 0
106
+ raise "Can't drop privileges as unprivileged user. Please run this command as a privileged user."
107
+ end
108
+
109
+ if runas_user.present?
110
+ uid = Etc.getpwnam(runas_user).uid
111
+ if (group = ProcessManager::Config.config[:group]) && group.present?
112
+ gid = Etc.getgrnam(group).gid
113
+ else
114
+ gid = Etc.getpwuid(uid).gid
115
+ end
116
+ Process.initgroups(runas_user, gid)
117
+ Process::GID.change_privilege(gid)
118
+ Process::UID.change_privilege(uid)
119
+ true
120
+ end
121
+ false
122
+ rescue Exception => e
123
+ $stderr.puts "Failed to drop privileges: #{e.class} - #{e.message} - #{e.backtrace.join("\n")}"
124
+ exit 1
125
+ end
126
+
127
+ def start
128
+ handle_pid_file
129
+ validate_ssl_config
130
+ trap_signals
131
+
132
+ spawn_children
133
+ puts "Started #{description} with #{ProcessManager::Config.config[:children]} children"
134
+ ProcessManager::Log.info("Started #{description} with #{ProcessManager::Config.config[:children]} children")
135
+
136
+ loop do
137
+ # master does nothing apart from replacing dead children
138
+ # and forwarding signals
139
+ sleep 1
140
+ end
141
+ end
142
+
143
+ def stop
144
+ if (pid = self.class.find_pid)
145
+ puts "Stopping #{description(pid)}"
146
+ ProcessManager::Log.info("Stopping #{description(pid)}")
147
+ begin
148
+ Process.kill('TERM', pid)
149
+ rescue Errno::ESRCH
150
+ end
151
+ else
152
+ puts "Nothing running that could be stopped"
153
+ end
154
+ end
155
+
156
+ def handle_pid_file
157
+ @file_lock ||= File.open(pid_lock_file, File::RDWR|File::CREAT, 0644)
158
+ lock_aquired = @file_lock.flock(File::LOCK_EX|File::LOCK_NB)
159
+
160
+ if lock_aquired == false
161
+ ProcessManager::Log.info("Could not aquire lock on #{pid_lock_file} - aborting start!")
162
+ self.class.abort
163
+
164
+ elsif File.exists?(pid_file)
165
+ pid = self.class.find_pid
166
+ if ProcessManager.process_running?(pid)
167
+ puts "Pidfile #{pid_file} exists and process #{pid} is running - aborting start!"
168
+ ProcessManager::Log.info("Pidfile #{pid_file} exists and process #{pid} is running - aborting start!")
169
+ @file_lock.close
170
+ self.class.abort
171
+ else
172
+ self.class.clean_stale_pid
173
+ end
174
+ end
175
+ ::SimplePid.drop(pid_file)
176
+ end
177
+
178
+ def spawn_children
179
+ ProcessManager::Config.config[:children].times do |i|
180
+ spawn_child(i)
181
+ sleep ProcessManager::Config.config[:wait_between_spawning_children].to_i
182
+ end
183
+ end
184
+
185
+ # spawn a new child and pass down out PID so that it can check if we are alive
186
+ def spawn_child(index)
187
+ master_pid = $$ # need to store in order to pass down to child
188
+ child_pid = fork do
189
+ child_class.new(index, master_pid).start
190
+ end
191
+ children[index] = child_pid
192
+ ProcessManager::Log.info "#{description}: Spawned child #{index + 1}/#{ProcessManager::Config.config[:children]}"
193
+ end
194
+
195
+ def trap_signals
196
+ # The QUIT & INT signals triggers a graceful shutdown.
197
+ # The master shuts down immediately and forwards the signal to each child
198
+ [:INT, :QUIT, :TERM].each do |sig|
199
+ trap(sig) do
200
+ ProcessManager::Log.info "#{description}: Received #{sig} - stopping children and shutting down"
201
+ kill_children(sig)
202
+ cleanup_and_exit
203
+ end
204
+ end
205
+
206
+ trap(:CHLD) do
207
+ handle_chld
208
+ end
209
+ end
210
+
211
+ def cleanup_and_exit
212
+ SimplePid.cleanup!(pid_file)
213
+ @file_lock.close
214
+ exit
215
+ end
216
+
217
+ def handle_chld
218
+ if child = reap_child
219
+ ProcessManager::Log.info "#{description}: Received CHLD - cleaning dead child process"
220
+ cleanup_dead_child(child)
221
+ else
222
+ ProcessManager::Log.debug "#{description}: Received CHLD - ignoring as it looks like a child of a child"
223
+ end
224
+ end
225
+
226
+ def reap_child
227
+ dead_child = nil
228
+ begin
229
+ dead_child = Process.wait
230
+ rescue Errno::ECHILD
231
+ end
232
+ dead_child
233
+ end
234
+
235
+ def cleanup_dead_child(dead_child)
236
+ ProcessManager::Log.info "#{description}: been told to replace child #{dead_child.inspect}"
237
+ # delete given child
238
+ if index = children.key(dead_child)
239
+ children.delete(index)
240
+ end
241
+
242
+ # check all other children
243
+ children.each do |child_index, child_pid|
244
+ begin
245
+ dead_child = Process.waitpid(child_pid, Process::WNOHANG)
246
+ if index = children.key(dead_child)
247
+ children.delete(index)
248
+ end
249
+ rescue Errno::ECHILD
250
+ end
251
+ end
252
+
253
+ replace_terminated_children
254
+ end
255
+
256
+ # make sure we have again as many child we need
257
+ def replace_terminated_children
258
+ missing_children = ProcessManager::Config.config[:children] - children.values.size
259
+ if missing_children > 0
260
+ ProcessManager::Log.info "#{description}: not enough child processes running - missing at least #{missing_children} - respawning"
261
+ 0.upto(ProcessManager::Config.config[:children] - 1).each do |i|
262
+ if children.has_key?(i)
263
+ ProcessManager::Log.debug "#{description}: child #{i+1}/#{ProcessManager::Config.config[:children]} is still there"
264
+ else
265
+ spawn_child(i)
266
+ end
267
+ end
268
+ else
269
+ ProcessManager::Log.debug "#{description}: no need to replace child processes"
270
+ end
271
+ end
272
+
273
+ def kill_children(sig)
274
+ children.each do |index, child_pid|
275
+ begin
276
+ Process.kill(sig, child_pid)
277
+ rescue Errno::ESRCH
278
+ end
279
+ end
280
+ end
281
+
282
+ def ensure_validate_configuration
283
+ if (errors = ProcessManager::Config.validate_config)
284
+ errors.each{|error| puts error}
285
+ end
286
+ cleanup_and_exit unless errors.empty?
287
+ end
288
+
289
+ def self.abort
290
+ Kernel.abort
291
+ end
292
+
293
+ def self.clean_stale_pid
294
+ puts "Pidfile #{pid_file} present but no matching process running - cleaning up"
295
+ ::FileUtils.rm(pid_file)
296
+ end
297
+
298
+ def self.find_pid
299
+ File.read(pid_file).chomp.to_i rescue nil
300
+ end
301
+
302
+ def description(pid = $$)
303
+ self.class.description(pid)
304
+ end
305
+
306
+ def child_class
307
+ self.class.child_class
308
+ end
309
+
310
+ # please override
311
+ def self.description(pid = $$)
312
+ "master #{pid}"
313
+ end
314
+
315
+ # please override
316
+ def self.child_class
317
+ ::ProcessManager::Daemon::Child
318
+ end
319
+
320
+ end
321
+ end
322
+ end