gregfitz23-chrono_trigger 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ == 0.0.4 2009-04-09
2
+ * 1 enhancement
3
+ * Added chrono_trigger binary and supporting files
4
+
1
5
  == 0.0.2 2009-04-02
2
6
 
3
7
  * 1 minor enhancement:
@@ -3,8 +3,11 @@ Manifest.txt
3
3
  PostInstall.txt
4
4
  README.rdoc
5
5
  Rakefile
6
+ bin/chrono_trigger
6
7
  lib/chrono_trigger.rb
7
8
  lib/chrono_trigger/cron_entry.rb
9
+ lib/chrono_trigger/process.rb
10
+ lib/chrono_trigger/runner.rb
8
11
  lib/chrono_trigger/shell.rb
9
12
  lib/chrono_trigger/tasks.rb
10
13
  lib/chrono_trigger/trigger.rb
@@ -1,3 +1 @@
1
- # Remember to add chrono_trigger.rake to lib/tasks in your rails project containing:
2
- # require 'chrono_trigger/tasks'
3
1
 
@@ -10,8 +10,6 @@ A cron framework for defining cron tasks using a readable DSL.
10
10
 
11
11
  == SYNOPSIS:
12
12
 
13
- After installation, create lib/tasks/chrono_trigger.rake containing: require 'chrono_trigger/tasks'
14
-
15
13
  Create trigger files (by default in the lib/triggers) directory.
16
14
  Triggers should follow the pattern:
17
15
 
@@ -22,7 +20,7 @@ trigger "name" do
22
20
  at :hour=>9, :minute=>[30,50]
23
21
  end
24
22
 
25
- Execute rake RAILS_ENV={env} chrono_trigger:run
23
+ Run chrono_trigger -t{full path to trigger file}
26
24
 
27
25
  == REQUIREMENTS:
28
26
 
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 3
2
+ :patch: 4
3
3
  :major: 0
4
4
  :minor: 0
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ require 'chrono_trigger/runner'
5
+
6
+ ChronoTrigger::Runner.run
7
+
@@ -0,0 +1,22 @@
1
+ module ChronoTrigger
2
+
3
+ class Process
4
+
5
+ def run(options={})
6
+ @t = Thread.new do
7
+ shell = ChronoTrigger::Shell.new
8
+ shell.load_triggers(options[:trigger_file])
9
+ loop do
10
+ shell.execute_triggers
11
+ sleep 1.minute.to_i
12
+ end
13
+ end
14
+
15
+ @t.join
16
+ end
17
+
18
+ def stop
19
+ @t.exit
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,276 @@
1
+ require File.join(File.dirname(__FILE__), 'process')
2
+ require "chrono_trigger"
3
+ require 'optparse'
4
+ require 'yaml'
5
+
6
+ module ChronoTrigger
7
+ class Runner
8
+
9
+ attr_accessor :options
10
+ private :options, :options=
11
+
12
+ def self.run
13
+ new
14
+ end
15
+
16
+ def self.shutdown
17
+ @@instance.shutdown
18
+ end
19
+
20
+ def initialize
21
+ @@instance = self
22
+ parse_options
23
+
24
+ @process = ProcessHelper.new(options[:logger], options[:pid_file], options[:user], options[:group])
25
+
26
+ if options[:stop]
27
+ @process.kill
28
+ exit(1)
29
+ end
30
+
31
+ pid = @process.running?
32
+ if pid
33
+ if options[:force]
34
+ STDERR.puts "Shutting down existing ChronoTrigger."
35
+ @process.kill
36
+ @process = ProcessHelper.new(options[:logger], options[:pid_file], options[:user], options[:group])
37
+ else
38
+ STDERR.puts "There is already a ChronoTrigger process running (pid #{pid}), exiting."
39
+ exit(1)
40
+ end
41
+ elsif pid.nil?
42
+ STDERR.puts "Cleaning up stale pidfile at #{options[:pid_file]}."
43
+ end
44
+
45
+ start
46
+ end
47
+
48
+ def parse_options
49
+ self.options = {
50
+ :log_level => Logger::INFO,
51
+ :daemonize => false,
52
+ :pid_file => File.join('', 'var', 'run', 'chrono_trigger.pid')
53
+ }
54
+
55
+ OptionParser.new do |opts|
56
+ opts.summary_width = 25
57
+
58
+ opts.banner = "ChronoTrigger\n\n",
59
+ "usage: chrono_trigger [options...]\n",
60
+ " chrono_trigger --help\n",
61
+ " chrono_trigger --version\n"
62
+
63
+ opts.separator ""
64
+ opts.separator ""; opts.separator "ChronoTrigger Options:"
65
+
66
+ opts.on("-tTRIGGER_FILE", "--triggers TRIGGERS", "Path to file specifying triggers to be executed") do |trigger_file|
67
+ options[:trigger_file] = trigger_file
68
+ end
69
+
70
+ opts.on("-f", "--force", "Force restart of ChronoTrigger process.") do
71
+ options[:force] = true
72
+ end
73
+
74
+ opts.on("-s", "--stop", "Stop a currently running ChronoTrigger process.") do
75
+ options[:stop] = true
76
+ end
77
+
78
+ opts.separator ""
79
+ opts.separator ""; opts.separator "Process:"
80
+
81
+ opts.on("-PFILE", "--pid FILENAME", "save PID in FILENAME when using -d option.", "(default: #{options[:pid_file]})") do |pid_file|
82
+ options[:pid_file] = File.expand_path(pid_file)
83
+ end
84
+
85
+ opts.on("-u", "--user USER", "User to run as") do |user|
86
+ options[:user] = user.to_i == 0 ? Etc.getpwnam(user).uid : user.to_i
87
+ end
88
+
89
+ opts.on("-gGROUP", "--group GROUP", "Group to run as") do |group|
90
+ options[:group] = group.to_i == 0 ? Etc.getgrnam(group).gid : group.to_i
91
+ end
92
+
93
+ opts.separator ""; opts.separator "Logging:"
94
+
95
+ opts.on("-L", "--log [FILE]", "Path to print debugging information.") do |log_path|
96
+ options[:logger] = File.expand_path(log_path)
97
+ end
98
+
99
+ opts.on("-v", "Increase logging verbosity (may be used multiple times).") do
100
+ options[:log_level] -= 1
101
+ end
102
+
103
+ opts.on("-d", "Run as a daemon.") do
104
+ options[:daemonize] = true
105
+ end
106
+ end.parse!
107
+ end
108
+
109
+ def start
110
+ drop_privileges
111
+
112
+ @process.daemonize if options[:daemonize]
113
+
114
+ setup_signal_traps
115
+ @process.write_pid_file
116
+
117
+ STDOUT.puts "Starting ChronoTrigger."
118
+ @chrono_trigger_process = ChronoTrigger::Process.new
119
+ @chrono_trigger_process.run(options)
120
+
121
+ @process.remove_pid_file
122
+ end
123
+
124
+ def drop_privileges
125
+ ::Process.egid = options[:group] if options[:group]
126
+ ::Process.euid = options[:user] if options[:user]
127
+ end
128
+
129
+ def shutdown
130
+ begin
131
+ STDOUT.puts "Shutting down."
132
+ @chrono_trigger_process.stop
133
+ exit(1)
134
+ rescue Object => e
135
+ STDERR.puts "There was an error shutting down: #{e}"
136
+ exit(70)
137
+ end
138
+ end
139
+
140
+ def setup_signal_traps
141
+ Signal.trap("INT") { shutdown }
142
+ Signal.trap("TERM") { shutdown }
143
+ end
144
+ end
145
+
146
+ class ProcessHelper
147
+
148
+ def initialize(log_file = nil, pid_file = nil, user = nil, group = nil)
149
+ @log_file = log_file
150
+ @pid_file = pid_file
151
+ @user = user
152
+ @group = group
153
+ end
154
+
155
+ def safefork
156
+ begin
157
+ if pid = fork
158
+ return pid
159
+ end
160
+ rescue Errno::EWOULDBLOCK
161
+ sleep 5
162
+ retry
163
+ end
164
+ end
165
+
166
+ def daemonize
167
+ sess_id = detach_from_terminal
168
+ exit if pid = safefork
169
+
170
+ Dir.chdir("/")
171
+ File.umask 0000
172
+
173
+ close_io_handles
174
+ redirect_io
175
+
176
+ return sess_id
177
+ end
178
+
179
+ def detach_from_terminal
180
+ srand
181
+ safefork and exit
182
+
183
+ unless sess_id = ::Process.setsid
184
+ raise "Couldn't detach from controlling terminal."
185
+ end
186
+
187
+ trap 'SIGHUP', 'IGNORE'
188
+
189
+ sess_id
190
+ end
191
+
192
+ def close_io_handles
193
+ ObjectSpace.each_object(IO) do |io|
194
+ unless [STDIN, STDOUT, STDERR].include?(io)
195
+ begin
196
+ io.close unless io.closed?
197
+ rescue Exception
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ def redirect_io
204
+ begin; STDIN.reopen('/dev/null'); rescue Exception; end
205
+
206
+ if @log_file
207
+ begin
208
+ STDOUT.reopen(@log_file, "a")
209
+ STDOUT.sync = true
210
+ rescue Exception
211
+ begin; STDOUT.reopen('/dev/null'); rescue Exception; end
212
+ end
213
+ else
214
+ begin; STDOUT.reopen('/dev/null'); rescue Exception; end
215
+ end
216
+
217
+ begin; STDERR.reopen(STDOUT); rescue Exception; end
218
+ STDERR.sync = true
219
+ end
220
+
221
+ def rescue_exception
222
+ begin
223
+ yield
224
+ rescue Exception
225
+ end
226
+ end
227
+
228
+ def write_pid_file
229
+ return unless @pid_file
230
+ FileUtils.mkdir_p(File.dirname(@pid_file))
231
+ File.open(@pid_file, "w") { |f| f.write(::Process.pid) }
232
+ File.chmod(0644, @pid_file)
233
+ end
234
+
235
+ def remove_pid_file
236
+ return unless @pid_file
237
+ File.unlink(@pid_file) if File.exists?(@pid_file)
238
+ end
239
+
240
+ def running?
241
+ return false unless @pid_file
242
+
243
+ pid = File.read(@pid_file).chomp.to_i rescue nil
244
+ pid = nil if pid == 0
245
+ return false unless pid
246
+
247
+ begin
248
+ ::Process.kill(0, pid)
249
+ return pid
250
+ rescue Errno::ESRCH
251
+ return nil
252
+ rescue Errno::EPERM
253
+ return pid
254
+ end
255
+ end
256
+
257
+ def kill
258
+ return false unless @pid_file
259
+
260
+ pid = File.read(@pid_file).chomp.to_i rescue nil
261
+ pid = nil if pid == 0
262
+ return false unless pid
263
+
264
+ begin
265
+ ::Process.kill("TERM", pid)
266
+ remove_pid_file
267
+ return pid
268
+ rescue Errno::ESRCH
269
+ return nil
270
+ rescue Errno::EPERM
271
+ return pid
272
+ end
273
+
274
+ end
275
+ end
276
+ end
@@ -25,6 +25,7 @@ module ChronoTrigger
25
25
  #Run execute on any trigger who's cron entry matches the current time.
26
26
  def execute_triggers
27
27
  now = Time.now
28
+ STDOUT.puts "triggers: #{triggers}"
28
29
  triggers.map {|trigger| trigger.execute_on_match(now)}
29
30
  end
30
31
 
@@ -55,6 +55,7 @@ module ChronoTrigger
55
55
  end
56
56
 
57
57
  def execute
58
+ STDOUT.puts "executing #{@exec_block}"
58
59
  @exec_block.call
59
60
  end
60
61
 
@@ -1,5 +1,5 @@
1
1
  trigger "trigger1" do
2
- runs { puts "trigger 1 runs every 1 minutes; executed at #{Time.now}"}
2
+ runs { File.open("/tmp/test_#{Time.now}.txt", 'w') {|file| file.puts "trigger 1 runs every 1 minutes; executed at #{Time.now}"}}
3
3
  every :minutes=>1
4
4
  end
5
5
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gregfitz23-chrono_trigger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Fitzgerald
@@ -9,14 +9,14 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-06 00:00:00 -07:00
13
- default_executable:
12
+ date: 2009-04-09 00:00:00 -07:00
13
+ default_executable: chrono_trigger
14
14
  dependencies: []
15
15
 
16
16
  description: TODO
17
17
  email: greg_fitz@yahoo.com
18
- executables: []
19
-
18
+ executables:
19
+ - chrono_trigger
20
20
  extensions: []
21
21
 
22
22
  extra_rdoc_files:
@@ -27,8 +27,11 @@ files:
27
27
  - PostInstall.txt
28
28
  - README.rdoc
29
29
  - VERSION.yml
30
+ - bin/chrono_trigger
30
31
  - lib/chrono_trigger
31
32
  - lib/chrono_trigger/cron_entry.rb
33
+ - lib/chrono_trigger/process.rb
34
+ - lib/chrono_trigger/runner.rb
32
35
  - lib/chrono_trigger/shell.rb
33
36
  - lib/chrono_trigger/tasks.rb
34
37
  - lib/chrono_trigger/trigger.rb