tootsie 0.9.3 → 0.9.4

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.
data/README.md CHANGED
@@ -180,7 +180,6 @@ Example S3 URLs:
180
180
  Current limitations
181
181
  ===================
182
182
 
183
- * Daemon supports only one task manager thread at a time.
184
183
  * Transcoding options are very basic.
185
184
  * No client access control; anyone can submit jobs.
186
185
 
@@ -216,8 +215,11 @@ Create a configuration, eg. `tootsie.conf`:
216
215
  aws_access_key_id: <your Amazon key>
217
216
  aws_secret_access_key: <your Amazon secret>
218
217
  sqs_queue_name: tootsie
218
+ pid_path: <where to write pid file>
219
+ log_path: <where to write log file>
220
+ worker_count: <number of workers>
219
221
 
220
- Start the task manager with `tootsie -c tootsie.conf start`. This will start Tootsie as a daemon.
222
+ Start the task manager with `tootsie -c tootsie.conf`.
221
223
 
222
224
  To run the web service, you will need a Rack-compatible web server, such as Unicorn or Thin. To start with Thin on port 9090:
223
225
 
@@ -3,72 +3,4 @@
3
3
  $:.unshift(File.expand_path('../../lib', __FILE__))
4
4
  require 'tootsie'
5
5
 
6
- num_workers = 4
7
- config_path = nil
8
- log_target = 'syslog'
9
-
10
- ARGV.options do |opts|
11
- opts.banner = "Usage: #{File.basename($0)} [OPTIONS] [start | stop | restart | status]"
12
- opts.separator ""
13
- opts.on("-n", "--num-workers=WORKERS", Integer,
14
- "Specify number of workers to fork (defaults to #{num_workers}.") do |value|
15
- num_workers = [1, value.to_i].max
16
- end
17
- opts.on("-c", "--config=PATH", String,
18
- "Specify configuration path.") do |value|
19
- config_path = value
20
- end
21
- opts.on("-l TARGET", "--logger TARGET", String,
22
- "Log to TARGET, which is either a file name or 'syslog'.") do |value|
23
- log_target = value
24
- end
25
- opts.on("-h", "--help", "Show this help message.") do
26
- puts opts
27
- exit
28
- end
29
- opts.parse!
30
- if ARGV.empty?
31
- puts "Nothing to do. Run with -h for help."
32
- exit
33
- end
34
- end
35
-
36
- unless config_path
37
- abort 'Configuration file not specified.'
38
- end
39
- config_path = File.expand_path(config_path)
40
-
41
- case log_target
42
- when 'syslog'
43
- logger = SyslogLogger.new('tootsie')
44
- else
45
- logger = Logger.new(log_target)
46
- end
47
-
48
- controller = Tootsie::Daemon.new(
49
- :root => File.join(File.dirname(__FILE__), "/.."),
50
- :pid_file => File.join(File.dirname(__FILE__), "/../tmp/task_manager.pid"),
51
- :logger => logger)
52
-
53
- spawner = Spawner.new(:num_children => num_workers, :logger => controller.logger)
54
-
55
- controller.on_spawn do
56
- $0 = "tootsie: master"
57
- spawner.on_spawn do
58
- $0 = "tootsie: worker"
59
- Signal.trap('TERM') do
60
- exit(2)
61
- end
62
- app = Tootsie::Application.new(:logger => controller.logger)
63
- app.configure!(config_path)
64
- begin
65
- app.task_manager.run!
66
- rescue SystemExit, Interrupt
67
- end
68
- end
69
- spawner.run
70
- end
71
- controller.on_terminate do
72
- spawner.terminate
73
- end
74
- controller.control(ARGV)
6
+ Tootsie::Runner.new.run!(ARGV)
@@ -8,7 +8,6 @@ require 'tootsie/application'
8
8
  require 'tootsie/client'
9
9
  require 'tootsie/configuration'
10
10
  require 'tootsie/command_runner'
11
- require 'tootsie/daemon'
12
11
  require 'tootsie/spawner'
13
12
  require 'tootsie/ffmpeg_adapter'
14
13
  require 'tootsie/image_metadata_extractor'
@@ -23,3 +22,4 @@ require 'tootsie/processors/image_processor'
23
22
  require 'tootsie/queues/sqs_queue'
24
23
  require 'tootsie/queues/file_system_queue'
25
24
  require 'tootsie/s3_utilities'
25
+ require 'tootsie/runner'
@@ -4,12 +4,19 @@ module Tootsie
4
4
 
5
5
  def initialize(options = {})
6
6
  @@instance = self
7
- @logger = options[:logger] || Logger.new($stderr)
8
7
  @configuration = Configuration.new
9
8
  end
10
9
 
11
10
  def configure!(config_path)
12
11
  @configuration.load_from_file(config_path)
12
+ case @configuration.log_path
13
+ when 'syslog'
14
+ @logger = SyslogLogger.new('tootsie')
15
+ when String
16
+ @logger = Logger.new(@configuration.log_path)
17
+ else
18
+ @logger = Logger.new($stderr)
19
+ end
13
20
  @queue = Tootsie::SqsQueue.new(@configuration.sqs_queue_name, sqs_service)
14
21
  @task_manager = TaskManager.new(@queue)
15
22
  end
@@ -4,13 +4,14 @@ module Tootsie
4
4
 
5
5
  def initialize
6
6
  @ffmpeg_thread_count = 1
7
+ @worker_count = 4
7
8
  @sqs_queue_name = 'tootsie'
8
9
  end
9
10
 
10
11
  def load_from_file(file_name)
11
12
  config = (YAML.load(File.read(file_name)) || {}).with_indifferent_access
12
13
  [:aws_access_key_id, :aws_secret_access_key, :ffmpeg_thread_count,
13
- :sqs_queue_name].each do |key|
14
+ :sqs_queue_name, :worker_count, :pid_path, :log_path].each do |key|
14
15
  if config.include?(key)
15
16
  value = config[key]
16
17
  value = $1.to_i if value =~ /\A\s*(\d+)\s*\z/
@@ -23,6 +24,9 @@ module Tootsie
23
24
  attr_accessor :aws_secret_access_key
24
25
  attr_accessor :ffmpeg_thread_count
25
26
  attr_accessor :sqs_queue_name
27
+ attr_accessor :pid_path
28
+ attr_accessor :log_path
29
+ attr_accessor :worker_count
26
30
 
27
31
  end
28
32
 
@@ -0,0 +1,118 @@
1
+ module Tootsie
2
+
3
+ # Runs the daemon from the command line.
4
+ class Runner
5
+
6
+ def initialize
7
+ @run_as_daemon = false
8
+ @config_path = '/etc/tootsie/tootsie.conf'
9
+ @app = Application.new
10
+ end
11
+
12
+ def run!(arguments = [])
13
+ OptionParser.new do |opts|
14
+ opts.banner = "Usage: #{File.basename($0)} [OPTIONS]"
15
+ opts.separator ""
16
+ opts.on("-d", "--daemon", 'Run as daemon') do
17
+ @run_as_daemon = true
18
+ end
19
+ opts.on("-p PATH", "--pid", "Store pid in PATH (defaults to #{@pid_path})") do |value|
20
+ @pid_path = File.expand_path(value)
21
+ end
22
+ opts.on("-c FILE", "--config FILE", "Read configuration from FILE (defaults to #{@config_path})") do |value|
23
+ @config_path = File.expand_path(value)
24
+ end
25
+ opts.on("-h", "--help", "Show this help.") do
26
+ puts opts
27
+ exit
28
+ end
29
+ opts.parse!(arguments)
30
+ end
31
+
32
+ @app.configure!(@config_path)
33
+
34
+ if @run_as_daemon
35
+ daemonize!
36
+ else
37
+ execute!
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def execute!
44
+ with_pid do
45
+ @spawner = Spawner.new(
46
+ :num_children => @app.configuration.worker_count,
47
+ :logger => logger)
48
+ @spawner.on_spawn do
49
+ $0 = "tootsie: worker"
50
+ Signal.trap('TERM') do
51
+ exit(2)
52
+ end
53
+ with_lifecycle_logging("Worker [#{Process.pid}]") do
54
+ @app.task_manager.run!
55
+ end
56
+ end
57
+ with_lifecycle_logging('Main process') do
58
+ @spawner.run
59
+ end
60
+ @spawner.terminate
61
+ end
62
+ end
63
+
64
+ def daemonize!(&block)
65
+ return Process.fork {
66
+ logger = @app.logger
67
+
68
+ Process.setsid
69
+ 0.upto(255) do |n|
70
+ File.for_fd(n, "r").close rescue nil
71
+ end
72
+
73
+ File.umask(27)
74
+ Dir.chdir('/')
75
+ $stdin.reopen("/dev/null", 'r')
76
+ $stdout.reopen("/dev/null", 'w')
77
+ $stderr.reopen("/dev/null", 'w')
78
+
79
+ Signal.trap("HUP") do
80
+ logger.debug("Ignoring SIGHUP")
81
+ end
82
+
83
+ execute!
84
+ }
85
+ end
86
+
87
+ def with_pid(&block)
88
+ path = @pid_path
89
+ path ||= @app.configuration.pid_path
90
+ path ||= '/var/run/tootsie.pid'
91
+
92
+ File.open(path, 'w') do |file|
93
+ file << Process.pid
94
+ end
95
+ begin
96
+ yield
97
+ ensure
98
+ File.delete(path) rescue nil
99
+ end
100
+ end
101
+
102
+ def with_lifecycle_logging(prefix, &block)
103
+ logger.info("#{prefix} starting")
104
+ yield
105
+ rescue SystemExit, Interrupt, SignalException
106
+ logger.info("#{prefix} signaled")
107
+ rescue Exception => e
108
+ logger.error("#{prefix} failed with exception #{e.class}: #{e}")
109
+ exit(1)
110
+ end
111
+
112
+ def logger
113
+ @app.logger
114
+ end
115
+
116
+ end
117
+
118
+ end
@@ -1,3 +1,3 @@
1
1
  module Tootsie
2
- VERSION = '0.9.3'
2
+ VERSION = '0.9.4'
3
3
  end
metadata CHANGED
@@ -1,266 +1,189 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: tootsie
3
- version: !ruby/object:Gem::Version
4
- hash: 61
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.4
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 9
9
- - 3
10
- version: 0.9.3
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Alexander Staubo
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-01-20 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2012-01-20 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: json
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &70337010813360 !ruby/object:Gem::Requirement
24
17
  none: false
25
- requirements:
18
+ requirements:
26
19
  - - ~>
27
- - !ruby/object:Gem::Version
28
- hash: 11
29
- segments:
30
- - 1
31
- - 4
32
- - 6
20
+ - !ruby/object:Gem::Version
33
21
  version: 1.4.6
34
22
  type: :runtime
35
- version_requirements: *id001
36
- - !ruby/object:Gem::Dependency
37
- name: sinatra
38
23
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *70337010813360
25
+ - !ruby/object:Gem::Dependency
26
+ name: sinatra
27
+ requirement: &70337010812840 !ruby/object:Gem::Requirement
40
28
  none: false
41
- requirements:
29
+ requirements:
42
30
  - - ~>
43
- - !ruby/object:Gem::Version
44
- hash: 15
45
- segments:
46
- - 1
47
- - 0
48
- version: "1.0"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.0'
49
33
  type: :runtime
50
- version_requirements: *id002
51
- - !ruby/object:Gem::Dependency
52
- name: activesupport
53
34
  prerelease: false
54
- requirement: &id003 !ruby/object:Gem::Requirement
35
+ version_requirements: *70337010812840
36
+ - !ruby/object:Gem::Dependency
37
+ name: activesupport
38
+ requirement: &70337010812360 !ruby/object:Gem::Requirement
55
39
  none: false
56
- requirements:
40
+ requirements:
57
41
  - - ~>
58
- - !ruby/object:Gem::Version
59
- hash: 7
60
- segments:
61
- - 3
62
- - 0
63
- - 0
42
+ - !ruby/object:Gem::Version
64
43
  version: 3.0.0
65
44
  type: :runtime
66
- version_requirements: *id003
67
- - !ruby/object:Gem::Dependency
68
- name: httpclient
69
45
  prerelease: false
70
- requirement: &id004 !ruby/object:Gem::Requirement
46
+ version_requirements: *70337010812360
47
+ - !ruby/object:Gem::Dependency
48
+ name: httpclient
49
+ requirement: &70337010811880 !ruby/object:Gem::Requirement
71
50
  none: false
72
- requirements:
51
+ requirements:
73
52
  - - ~>
74
- - !ruby/object:Gem::Version
75
- hash: 5
76
- segments:
77
- - 2
78
- - 2
79
- - 1
53
+ - !ruby/object:Gem::Version
80
54
  version: 2.2.1
81
55
  type: :runtime
82
- version_requirements: *id004
83
- - !ruby/object:Gem::Dependency
84
- name: builder
85
56
  prerelease: false
86
- requirement: &id005 !ruby/object:Gem::Requirement
57
+ version_requirements: *70337010811880
58
+ - !ruby/object:Gem::Dependency
59
+ name: builder
60
+ requirement: &70337010811400 !ruby/object:Gem::Requirement
87
61
  none: false
88
- requirements:
62
+ requirements:
89
63
  - - ~>
90
- - !ruby/object:Gem::Version
91
- hash: 15
92
- segments:
93
- - 2
94
- - 1
95
- - 2
64
+ - !ruby/object:Gem::Version
96
65
  version: 2.1.2
97
66
  type: :runtime
98
- version_requirements: *id005
99
- - !ruby/object:Gem::Dependency
100
- name: mime-types
101
67
  prerelease: false
102
- requirement: &id006 !ruby/object:Gem::Requirement
68
+ version_requirements: *70337010811400
69
+ - !ruby/object:Gem::Dependency
70
+ name: mime-types
71
+ requirement: &70337010810920 !ruby/object:Gem::Requirement
103
72
  none: false
104
- requirements:
73
+ requirements:
105
74
  - - ~>
106
- - !ruby/object:Gem::Version
107
- hash: 47
108
- segments:
109
- - 1
110
- - 16
111
- version: "1.16"
75
+ - !ruby/object:Gem::Version
76
+ version: '1.16'
112
77
  type: :runtime
113
- version_requirements: *id006
114
- - !ruby/object:Gem::Dependency
115
- name: xml-simple
116
78
  prerelease: false
117
- requirement: &id007 !ruby/object:Gem::Requirement
79
+ version_requirements: *70337010810920
80
+ - !ruby/object:Gem::Dependency
81
+ name: xml-simple
82
+ requirement: &70337010810440 !ruby/object:Gem::Requirement
118
83
  none: false
119
- requirements:
84
+ requirements:
120
85
  - - ~>
121
- - !ruby/object:Gem::Version
122
- hash: 15
123
- segments:
124
- - 1
125
- - 0
126
- - 12
86
+ - !ruby/object:Gem::Version
127
87
  version: 1.0.12
128
88
  type: :runtime
129
- version_requirements: *id007
130
- - !ruby/object:Gem::Dependency
131
- name: thin
132
89
  prerelease: false
133
- requirement: &id008 !ruby/object:Gem::Requirement
90
+ version_requirements: *70337010810440
91
+ - !ruby/object:Gem::Dependency
92
+ name: thin
93
+ requirement: &70337010809960 !ruby/object:Gem::Requirement
134
94
  none: false
135
- requirements:
95
+ requirements:
136
96
  - - ~>
137
- - !ruby/object:Gem::Version
138
- hash: 17
139
- segments:
140
- - 1
141
- - 2
142
- - 7
97
+ - !ruby/object:Gem::Version
143
98
  version: 1.2.7
144
99
  type: :runtime
145
- version_requirements: *id008
146
- - !ruby/object:Gem::Dependency
147
- name: s3
148
100
  prerelease: false
149
- requirement: &id009 !ruby/object:Gem::Requirement
101
+ version_requirements: *70337010809960
102
+ - !ruby/object:Gem::Dependency
103
+ name: s3
104
+ requirement: &70337010809480 !ruby/object:Gem::Requirement
150
105
  none: false
151
- requirements:
106
+ requirements:
152
107
  - - ~>
153
- - !ruby/object:Gem::Version
154
- hash: 29
155
- segments:
156
- - 0
157
- - 3
158
- - 7
108
+ - !ruby/object:Gem::Version
159
109
  version: 0.3.7
160
110
  type: :runtime
161
- version_requirements: *id009
162
- - !ruby/object:Gem::Dependency
163
- name: sqs
164
111
  prerelease: false
165
- requirement: &id010 !ruby/object:Gem::Requirement
112
+ version_requirements: *70337010809480
113
+ - !ruby/object:Gem::Dependency
114
+ name: sqs
115
+ requirement: &70337010809000 !ruby/object:Gem::Requirement
166
116
  none: false
167
- requirements:
117
+ requirements:
168
118
  - - ~>
169
- - !ruby/object:Gem::Version
170
- hash: 31
171
- segments:
172
- - 0
173
- - 1
174
- - 2
119
+ - !ruby/object:Gem::Version
175
120
  version: 0.1.2
176
121
  type: :runtime
177
- version_requirements: *id010
178
- - !ruby/object:Gem::Dependency
179
- name: unicorn
180
122
  prerelease: false
181
- requirement: &id011 !ruby/object:Gem::Requirement
123
+ version_requirements: *70337010809000
124
+ - !ruby/object:Gem::Dependency
125
+ name: unicorn
126
+ requirement: &70337010808520 !ruby/object:Gem::Requirement
182
127
  none: false
183
- requirements:
128
+ requirements:
184
129
  - - ~>
185
- - !ruby/object:Gem::Version
186
- hash: 57
187
- segments:
188
- - 4
189
- - 1
190
- - 1
130
+ - !ruby/object:Gem::Version
191
131
  version: 4.1.1
192
132
  type: :runtime
193
- version_requirements: *id011
194
- - !ruby/object:Gem::Dependency
195
- name: i18n
196
133
  prerelease: false
197
- requirement: &id012 !ruby/object:Gem::Requirement
134
+ version_requirements: *70337010808520
135
+ - !ruby/object:Gem::Dependency
136
+ name: i18n
137
+ requirement: &70337010808040 !ruby/object:Gem::Requirement
198
138
  none: false
199
- requirements:
200
- - - ">="
201
- - !ruby/object:Gem::Version
202
- hash: 11
203
- segments:
204
- - 0
205
- - 4
206
- - 2
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
207
142
  version: 0.4.2
208
143
  type: :runtime
209
- version_requirements: *id012
210
- - !ruby/object:Gem::Dependency
211
- name: scashin133-syslog_logger
212
144
  prerelease: false
213
- requirement: &id013 !ruby/object:Gem::Requirement
145
+ version_requirements: *70337010808040
146
+ - !ruby/object:Gem::Dependency
147
+ name: scashin133-syslog_logger
148
+ requirement: &70337010807560 !ruby/object:Gem::Requirement
214
149
  none: false
215
- requirements:
150
+ requirements:
216
151
  - - ~>
217
- - !ruby/object:Gem::Version
218
- hash: 13
219
- segments:
220
- - 1
221
- - 7
222
- - 3
152
+ - !ruby/object:Gem::Version
223
153
  version: 1.7.3
224
154
  type: :runtime
225
- version_requirements: *id013
226
- - !ruby/object:Gem::Dependency
227
- name: rspec
228
155
  prerelease: false
229
- requirement: &id014 !ruby/object:Gem::Requirement
156
+ version_requirements: *70337010807560
157
+ - !ruby/object:Gem::Dependency
158
+ name: rspec
159
+ requirement: &70337010807180 !ruby/object:Gem::Requirement
230
160
  none: false
231
- requirements:
232
- - - ">="
233
- - !ruby/object:Gem::Version
234
- hash: 3
235
- segments:
236
- - 0
237
- version: "0"
161
+ requirements:
162
+ - - ! '>='
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
238
165
  type: :development
239
- version_requirements: *id014
240
- - !ruby/object:Gem::Dependency
241
- name: rake
242
166
  prerelease: false
243
- requirement: &id015 !ruby/object:Gem::Requirement
167
+ version_requirements: *70337010807180
168
+ - !ruby/object:Gem::Dependency
169
+ name: rake
170
+ requirement: &70337010806720 !ruby/object:Gem::Requirement
244
171
  none: false
245
- requirements:
246
- - - ">="
247
- - !ruby/object:Gem::Version
248
- hash: 3
249
- segments:
250
- - 0
251
- version: "0"
172
+ requirements:
173
+ - - ! '>='
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
252
176
  type: :development
253
- version_requirements: *id015
177
+ prerelease: false
178
+ version_requirements: *70337010806720
254
179
  description: Tootsie is a simple audio/video/image transcoding/modification application.
255
- email:
180
+ email:
256
181
  - alex@origo.no
257
- executables:
182
+ executables:
258
183
  - tootsie
259
184
  extensions: []
260
-
261
185
  extra_rdoc_files: []
262
-
263
- files:
186
+ files:
264
187
  - .gitignore
265
188
  - Gemfile
266
189
  - License
@@ -269,13 +192,11 @@ files:
269
192
  - Tootsie.gemspec
270
193
  - bin/tootsie
271
194
  - config.ru
272
- - config/development-sample.yml
273
195
  - lib/tootsie.rb
274
196
  - lib/tootsie/application.rb
275
197
  - lib/tootsie/client.rb
276
198
  - lib/tootsie/command_runner.rb
277
199
  - lib/tootsie/configuration.rb
278
- - lib/tootsie/daemon.rb
279
200
  - lib/tootsie/ffmpeg_adapter.rb
280
201
  - lib/tootsie/image_metadata_extractor.rb
281
202
  - lib/tootsie/input.rb
@@ -284,6 +205,7 @@ files:
284
205
  - lib/tootsie/processors/video_processor.rb
285
206
  - lib/tootsie/queues/file_system_queue.rb
286
207
  - lib/tootsie/queues/sqs_queue.rb
208
+ - lib/tootsie/runner.rb
287
209
  - lib/tootsie/s3_utilities.rb
288
210
  - lib/tootsie/spawner.rb
289
211
  - lib/tootsie/task_manager.rb
@@ -298,38 +220,29 @@ files:
298
220
  - spec/tootsie/s3_utilities_spec.rb
299
221
  homepage: http://github.com/alexstaubo/tootsie
300
222
  licenses: []
301
-
302
223
  post_install_message:
303
224
  rdoc_options: []
304
-
305
- require_paths:
225
+ require_paths:
306
226
  - lib
307
- required_ruby_version: !ruby/object:Gem::Requirement
227
+ required_ruby_version: !ruby/object:Gem::Requirement
308
228
  none: false
309
- requirements:
310
- - - ">="
311
- - !ruby/object:Gem::Version
312
- hash: 3
313
- segments:
314
- - 0
315
- version: "0"
316
- required_rubygems_version: !ruby/object:Gem::Requirement
229
+ requirements:
230
+ - - ! '>='
231
+ - !ruby/object:Gem::Version
232
+ version: '0'
233
+ required_rubygems_version: !ruby/object:Gem::Requirement
317
234
  none: false
318
- requirements:
319
- - - ">="
320
- - !ruby/object:Gem::Version
321
- hash: 3
322
- segments:
323
- - 0
324
- version: "0"
235
+ requirements:
236
+ - - ! '>='
237
+ - !ruby/object:Gem::Version
238
+ version: '0'
325
239
  requirements: []
326
-
327
240
  rubyforge_project: tootsie
328
241
  rubygems_version: 1.8.6
329
242
  signing_key:
330
243
  specification_version: 3
331
244
  summary: Tootsie is a simple audio/video/image transcoding/modification application.
332
- test_files:
245
+ test_files:
333
246
  - spec/spec_helper.rb
334
247
  - spec/test_files/BF 0622 1820.tif
335
248
  - spec/tootsie/command_runner_spec.rb
@@ -1,4 +0,0 @@
1
- ---
2
- aws_access_key_id: <your_key>
3
- aws_secret_access_key: <your_secret>
4
- sqs_queue_name: tootsie
@@ -1,282 +0,0 @@
1
- require "logger"
2
- require "fileutils"
3
- require 'optparse'
4
-
5
- module Tootsie
6
-
7
- class DaemonError < Exception; end
8
- class DaemonAlreadyRunning < DaemonError; end
9
- class DaemonNotRunning < DaemonError; end
10
- class DaemonTerminationFailed < DaemonError; end
11
- class DaemonNotConfigured < DaemonError; end
12
- class DaemonStartFailed < DaemonError; end
13
-
14
- # Daemon controller class that encapsulates a running daemon and a remote interface to it.
15
- class Daemon
16
-
17
- # Initializes the daemon controller.
18
- def initialize(options = {})
19
- @root = options[:root] || Dir.pwd
20
- @pid_file = options[:pid_file]
21
- @logger = options[:logger] || Logger.new('/dev/null')
22
- @on_spawn = nil
23
- end
24
-
25
- # Specifies a block to execute to run the actual daemon. Each call overrides the previous one.
26
- def on_spawn(&block)
27
- @on_spawn = block
28
- end
29
-
30
- # Specifies a block to execute to termiantion. Each call overrides the previous one.
31
- def on_terminate(&block)
32
- @on_terminate = block
33
- end
34
-
35
- # Control the daemon through command-line arguments.
36
- def control(args, title = nil)
37
- $stderr.sync = true
38
- title ||= File.basename($0)
39
- command = args.shift
40
- control_with_command(command, args, title)
41
- end
42
-
43
- # Control the daemon through a specific command.
44
- def control_with_command(command, args, title = nil)
45
- case command
46
- when "start"
47
- $stderr << "Starting #{title}: "
48
- handle_errors do
49
- start
50
- $stderr << "started\n"
51
- end
52
- when "stop"
53
- $stderr << "Stopping #{title}: "
54
- options = {}
55
- handle_errors do
56
- stop({:signal => "TERM"}.merge(options))
57
- $stderr << "stopped\n"
58
- end
59
- when "restart"
60
- $stderr << "Restarting #{title}: "
61
- handle_errors do
62
- restart
63
- $stderr << "restarted\n"
64
- end
65
- when "status"
66
- if running?
67
- $stderr << "#{title} is running\n"
68
- else
69
- $stderr << "#{title} is not running\n"
70
- end
71
- else
72
- if command
73
- $stderr << "#{File.basename($0)}: Invalid command #{command}\n"
74
- else
75
- puts "Usage: #{File.basename($0)} [start | stop | restart | status]"
76
- end
77
- end
78
- end
79
-
80
- # Starts daemon.
81
- def start
82
- raise DaemonNotConfigured, "Daemon not configured" unless @on_spawn
83
- FileUtils.mkdir_p(File.dirname(@pid_file)) rescue nil
84
- pid = self.pid
85
- if pid
86
- if pid_running?(pid)
87
- raise DaemonAlreadyRunning, "Process is already running with pid #{pid}"
88
- end
89
- end
90
- File.delete(@pid_file) rescue nil
91
- child_pid = Process.fork
92
- if child_pid
93
- sleep(1)
94
- unless running?
95
- pid = self.pid
96
- if pid == child_pid
97
- raise DaemonStartFailed, "Daemon started, but failed prematurely"
98
- else
99
- raise DaemonStartFailed, "Daemon failed to start for some unknown reason"
100
- end
101
- end
102
- return
103
- end
104
- class << logger
105
- def format_message(severity, timestamp, progname, msg)
106
- "[#{timestamp}] #{msg}\n"
107
- end
108
- end
109
- logger.info("Starting")
110
- begin
111
- Process.setsid
112
- 0.upto(255) do |n|
113
- File.for_fd(n, "r").close rescue nil
114
- end
115
- File.umask(27)
116
- Dir.chdir(@root)
117
- $stdin = File.open("/dev/null", File::RDWR)
118
- $stdout = File.open("/dev/null", File::RDWR)
119
- $stderr = File.open("/dev/null", File::RDWR)
120
- @pid = Process.pid
121
- File.open(@pid_file, "w") do |file|
122
- file << Process.pid
123
- end
124
- Signal.trap("HUP") do
125
- logger.debug("Ignoring SIGHUP")
126
- end
127
- Signal.trap("TERM") do
128
- if $$ == @pid
129
- logger.info("Terminating (#{$$})")
130
- @on_terminate.call if @on_terminate
131
- File.delete(@pid_file) rescue nil
132
- else
133
- # Was sent to a child
134
- end
135
- exit(0)
136
- end
137
- @on_spawn.call
138
- exit(0)
139
- rescue SystemExit
140
- # Do nothing
141
- rescue Exception => e
142
- message = "#{e.message}\n"
143
- message << e.backtrace.map { |line| "\tfrom #{line}\n" }.join
144
- logger.error(message)
145
- exit(1)
146
- ensure
147
- logger.close
148
- end
149
- end
150
-
151
- # Stops daemon.
152
- def stop(options = {})
153
- stopped = false
154
- found = false
155
- pid = self.pid
156
- if pid
157
- # Send TERM to process
158
- begin
159
- Process.kill(options[:signal] || "TERM", pid)
160
- rescue Errno::ESRCH
161
- stopped = true
162
- rescue Exception => e
163
- raise DaemonTerminationFailed, "Could not stop process #{pid}: #{e.message}"
164
- end
165
- unless stopped
166
- # Process was signaled, now wait for it to die
167
- found = true
168
- 30.times do
169
- begin
170
- if not pid_running?(pid)
171
- stopped = true
172
- break
173
- end
174
- sleep(1)
175
- rescue Exception => e
176
- raise DaemonTerminationFailed, "Could not stop process #{pid}: #{e.message}"
177
- end
178
- end
179
- if found and not stopped
180
- # Process still running after wait, force kill and wait
181
- begin
182
- Process.kill("KILL", pid)
183
- rescue Errno::ESRCH
184
- stopped = true
185
- end
186
- 30.times do
187
- begin
188
- if not pid_running?(pid)
189
- stopped = true
190
- break
191
- end
192
- sleep(1)
193
- rescue Exception => e
194
- raise DaemonTerminationFailed, "Could not stop process #{pid}: #{e.message}"
195
- end
196
- end
197
- if not stopped
198
- raise DaemonTerminationFailed, "Timeout attempting to stop process #{pid}"
199
- end
200
- end
201
- end
202
- end
203
- unless found
204
- raise DaemonNotRunning, "Process is not running"
205
- end
206
- end
207
-
208
- # Restarts daemon.
209
- def restart
210
- if running?
211
- begin
212
- stop
213
- rescue DaemonNotRunning
214
- # Ignore
215
- end
216
- end
217
- start
218
- end
219
-
220
- # Is the daemon running?
221
- def running?
222
- !pid.nil?
223
- end
224
-
225
- # Returns the daemon pid.
226
- def pid
227
- pid = nil
228
- maybe_pid = File.read(@pid_file) rescue nil
229
- if maybe_pid =~ /([0-9]+)/
230
- maybe_pid = $1.to_i
231
- begin
232
- Process.kill(0, maybe_pid)
233
- rescue Errno::ESRCH
234
- else
235
- pid = maybe_pid
236
- end
237
- end
238
- pid
239
- end
240
-
241
- # Signals the daemon.
242
- def signal(signal)
243
- pid = self.pid
244
- if pid
245
- Process.kill(signal, pid)
246
- else
247
- raise DaemonNotRunning, "Process is not running"
248
- end
249
- end
250
-
251
- attr_reader :root
252
- attr_reader :pid_file
253
- attr_reader :logger
254
-
255
- private
256
-
257
- def pid_running?(pid)
258
- begin
259
- Process.kill(0, pid)
260
- rescue Errno::ESRCH
261
- return false
262
- end
263
- return true
264
- end
265
-
266
- def handle_errors(&block)
267
- begin
268
- yield
269
- rescue DaemonError => e
270
- $stderr << "#{e.message}\n"
271
- if e.is_a?(DaemonAlreadyRunning) or e.is_a?(DaemonNotRunning)
272
- exit_code = 0
273
- else
274
- exit_code = 1
275
- end
276
- exit(exit_code)
277
- end
278
- end
279
-
280
- end
281
-
282
- end