daemonizr 0.1.0

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.
@@ -0,0 +1 @@
1
+ v0.1.0. Initial release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 Carl Mercier
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,7 @@
1
+ CHANGELOG
2
+ LICENSE
3
+ Manifest
4
+ README
5
+ Rakefile
6
+ daemonizr.rb
7
+ lib/daemonizr.rb
data/README ADDED
@@ -0,0 +1,7 @@
1
+ = Daemonizr - Process forking and monitoring for mere mortals
2
+
3
+ An example is worth a thousand words...
4
+
5
+ d = Daemonizr.new("Daemonizr")
6
+ d.start_cluster("MyServer", 3, lambda {loop{ sleep 2; File.open("/tmp/daemonizr.log", "a") { |f| f.puts "#{Process.pid}: #{Time.now}" } }})
7
+ d.monitor_cluster! # Will hang here until the process is terminated with SIGTERM (kill 5). If a fork dies, it will be restarted.
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('daemonizr') do |p|
6
+ p.description = "Process forking and monitoring for mere mortals"
7
+ p.url = "http://github.com/cmer/daemonizr"
8
+ p.author = "Carl Mercier"
9
+ p.email = "carl@carlmercier.com"
10
+ p.ignore_pattern = ["tmp/*", "script/*", "pkg/*"]
11
+ p.development_dependencies = []
12
+ end
13
+
14
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{daemonizr}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Carl Mercier"]
9
+ s.date = %q{2009-11-05}
10
+ s.description = %q{Process forking and monitoring for mere mortals}
11
+ s.email = %q{carl@carlmercier.com}
12
+ s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README", "lib/daemonizr.rb"]
13
+ s.files = ["CHANGELOG", "LICENSE", "Manifest", "README", "Rakefile", "daemonizr.rb", "lib/daemonizr.rb", "daemonizr.gemspec"]
14
+ s.homepage = %q{http://github.com/cmer/daemonizr}
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Daemonizr", "--main", "README"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{daemonizr}
18
+ s.rubygems_version = %q{1.3.5}
19
+ s.summary = %q{Process forking and monitoring for mere mortals}
20
+
21
+ if s.respond_to? :specification_version then
22
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
+ s.specification_version = 3
24
+
25
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
26
+ else
27
+ end
28
+ else
29
+ end
30
+ end
@@ -0,0 +1 @@
1
+ require 'lib/daemonizr'
@@ -0,0 +1,159 @@
1
+ require 'rubygems'
2
+ require 'daemons'
3
+ require 'logger'
4
+
5
+ class Daemonizr
6
+ CLUSTERS = {}
7
+
8
+ def initialize(instance_name, options = {})
9
+ @name = instance_name
10
+ @pid_path = options[:pid_path]
11
+ @log_to_stdout = true
12
+
13
+ if options[:log_file].is_a?(String)
14
+ raise ArgumentError.new("Invalid log_level value.") unless [:debug, :info, :warn, :error, :fatal, nil].include?(options[:log_level])
15
+ @logger = Logger.new(options[:log_file])
16
+ @logger.level = eval("Logger::#{(options[:log_level] || :info).to_s.upcase}")
17
+ end
18
+ end
19
+
20
+ def start_cluster(name, count, prok)
21
+ log :info, "Starting new cluster named '#{name}' with #{count} processes..."
22
+ raise "Can't start another cluster with the name #{name}. Already running." if cluster_exists?(name)
23
+ CLUSTERS[name] = { :count => count, :proc => prok, :processes => {} }
24
+ (1..count).each do |id|
25
+ log :debug, "Asking to start process #{id}/#{count} for cluster '#{name}'..."
26
+ start_daemon(name, id)
27
+ end
28
+ true
29
+ end
30
+
31
+ def stop_all_clusters
32
+ log :debug, "Stopping all clusters..."
33
+ CLUSTERS.each_key do |k|
34
+ stop_cluster(k)
35
+ end
36
+ end
37
+
38
+ def monitor_cluster!(interval = 5)
39
+ log :info, "Monitoring of clusters starting..."
40
+ while "ruby" > "java" do
41
+ CLUSTERS.each_key do |cluster_name|
42
+ CLUSTERS[cluster_name][:processes].each_key do |process_id|
43
+ unless CLUSTERS[cluster_name][:processes][process_id].running?
44
+ log :warn, "Process ##{process_id} of cluster '#{cluster_name}' is not running. Restarting..."
45
+ start_daemon(cluster_name, process_id)
46
+ end
47
+ end
48
+ end
49
+
50
+ begin
51
+ sleep interval
52
+ rescue Interrupt
53
+ # Process was killed. Stop clusters.
54
+ puts "\n"
55
+ log :info, "Received SIGTERM. Stopping all clusters and stop monitoring."
56
+ stop_all_clusters
57
+ log :info, "Monitoring stopped."
58
+ return true
59
+ end
60
+ end
61
+ end
62
+
63
+
64
+ protected
65
+ def start_daemon(name, id)
66
+ if CLUSTERS[name][:processes][id] && CLUSTERS[name][:processes][id].running?
67
+ log :error, "Process ##{id} for cluster '#{name}' is already running. Can't start it again."
68
+ raise ClusterAlreadyRunningException
69
+ else
70
+ # remove_stale_pid_file(name, id)
71
+ process_title = "#{@name}: #{name} #{id}/#{CLUSTERS[name][:count]}"
72
+ log :info, "Starting process '#{process_title}'..."
73
+ prok = CLUSTERS[name][:proc]
74
+
75
+ CLUSTERS[name][:processes][id] = Daemons.call(:multiple=>true) do
76
+ # TODO: figure out why it gets truncated
77
+ $PROGRAM_NAME = process_title
78
+ # write_pid_file(name, id)
79
+ prok.call
80
+ end
81
+
82
+ log :info, "Process '#{process_title}' started with PID #{CLUSTERS[name][:processes][id].pid.pid}."
83
+ return CLUSTERS[name][:processes][id]
84
+ end
85
+ end
86
+
87
+ def stop_cluster(name)
88
+ log :info, "Stopping cluster '#{name}'..."
89
+ raise ClusterDoesNotExistException unless cluster_exists?(name)
90
+
91
+ CLUSTERS[name][:processes].each_key do |k|
92
+ log :debug, "Stopping process ##{k} of cluster '#{name}'..."
93
+ CLUSTERS[name][:processes][k].stop
94
+ # remove_stale_pid_file(name, k)
95
+ end
96
+
97
+ CLUSTERS.delete(name); true
98
+ end
99
+
100
+ def cluster_exists?(name)
101
+ return CLUSTERS.keys.include?(name)
102
+ end
103
+
104
+ def log(severity, msg)
105
+ msg = format_log_message(msg)
106
+ @logger.send(severity, msg) if @logger
107
+ puts msg if @log_to_stdout || !@logger
108
+ end
109
+
110
+ def format_log_message(msg)
111
+ msg
112
+ end
113
+
114
+ # def pid_file(cluster, id)
115
+ # if @pid_path
116
+ # f = @pid_path.strip
117
+ # f += File::SEPARATOR unless f[-1,1] == File::SEPARATOR
118
+ # f += "#{@name}-#{cluster}-#{id}.pid"
119
+ # else
120
+ # nil
121
+ # end
122
+ # end
123
+ #
124
+ # def write_pid_file(name, id)
125
+ # pf = pid_file(name, id)
126
+ # if pf
127
+ # log :debug, "Writing PID to #{pf}"
128
+ # FileUtils.mkdir_p File.dirname(pf)
129
+ # open(pf,"w") { |f| f.write(Process.pid) }
130
+ # File.chmod(0644, pf)
131
+ # end
132
+ # end
133
+ #
134
+ # def remove_stale_pid_file(name, id)
135
+ # pf = pid_file(name, id)
136
+ # if File.exist?(pf)
137
+ # pid = File.open(pf).read
138
+ # File.delete(pf) unless pid && process_running?(pid)
139
+ # end
140
+ # end
141
+ #
142
+ # def process_running?(pid)
143
+ # begin
144
+ # Process.kill(0, pid.to_i)
145
+ # true
146
+ # rescue Errno::ESRCH
147
+ # false
148
+ # end
149
+ # end
150
+
151
+ class ClusterAlreadyRunningException < Exception; end
152
+ class ClusterDoesNotExistException < Exception; end
153
+ end
154
+
155
+
156
+
157
+ # d = Daemonizr.new("Daemonizr", :pid_path => "/tmp/")
158
+ # d.start_cluster("MyServer", 3, lambda {loop{ sleep 2; File.open("/tmp/daemonizr.log", "a") { |f| f.puts "#{Process.pid}: #{Time.now}" } }})
159
+ # d.monitor_cluster! # Will hang here until the process is terminated with SIGTERM (kill 5).
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: daemonizr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Carl Mercier
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-05 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Process forking and monitoring for mere mortals
17
+ email: carl@carlmercier.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - CHANGELOG
24
+ - LICENSE
25
+ - README
26
+ - lib/daemonizr.rb
27
+ files:
28
+ - CHANGELOG
29
+ - LICENSE
30
+ - Manifest
31
+ - README
32
+ - Rakefile
33
+ - daemonizr.rb
34
+ - lib/daemonizr.rb
35
+ - daemonizr.gemspec
36
+ has_rdoc: true
37
+ homepage: http://github.com/cmer/daemonizr
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options:
42
+ - --line-numbers
43
+ - --inline-source
44
+ - --title
45
+ - Daemonizr
46
+ - --main
47
+ - README
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "1.2"
61
+ version:
62
+ requirements: []
63
+
64
+ rubyforge_project: daemonizr
65
+ rubygems_version: 1.3.5
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: Process forking and monitoring for mere mortals
69
+ test_files: []
70
+