sanford 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9e76d785a8dc8ecf399b69798c6671ea3cc59661
4
+ data.tar.gz: 9ffe75e092d9672b18f796e3702751f3b7b8f4dd
5
+ SHA512:
6
+ metadata.gz: 8ab100e15c7772f82d3a5aebb52418c80af556596cc186b13a7559b99fcc02085fdb91c2504ef637957bedae4e661e9248ebf7ea25b3eed74fc0630816689651
7
+ data.tar.gz: fa68f9c299344ef106bd52c836c7d60d28c2c41bd6d46342c238e1a256d79528c651e0ae2df1a1419d97ddb1c5e7c18b88258468c2f2eda37a1cdb42d363b25c
data/Gemfile CHANGED
@@ -5,4 +5,5 @@ gemspec
5
5
  gem 'rake'
6
6
  gem 'pry'
7
7
 
8
+ gem 'bson'
8
9
  gem 'bson_ext', '~>1.7'
data/lib/sanford/cli.rb CHANGED
@@ -1,6 +1,4 @@
1
1
  require 'sanford'
2
- require 'sanford/host_data'
3
- require 'sanford/server'
4
2
  require 'sanford/version'
5
3
 
6
4
  module Sanford
@@ -26,6 +24,7 @@ module Sanford
26
24
  @command = @cli.args.first || 'run'
27
25
  Sanford.config.services_file = @cli.opts['config'] if @cli.opts['config']
28
26
  Sanford.init
27
+ require 'sanford/manager'
29
28
  Sanford::Manager.call(@command, @cli.opts)
30
29
  rescue CLIRB::HelpExit
31
30
  puts help
@@ -52,251 +51,6 @@ module Sanford
52
51
 
53
52
  end
54
53
 
55
- module Manager
56
-
57
- def self.call(action, options = nil)
58
- get_handler_class(action).new(options).tap{ |manager| manager.send(action) }
59
- end
60
-
61
- def self.get_handler_class(action)
62
- case action.to_sym
63
- when :start, :run
64
- ServerHandler
65
- when :stop, :restart
66
- SignalHandler
67
- end
68
- end
69
-
70
- class ServerHandler
71
-
72
- def initialize(options = nil)
73
- @config = Config.new(options)
74
- raise Sanford::NoHostError.new(@config.host_name) if !@config.found_host?
75
- raise Sanford::InvalidHostError.new(@config.host) if !@config.has_listen_args?
76
- @host = @config.host
77
- @logger = @host.logger
78
-
79
- @server_options = {}
80
- # FUTURE allow passing through dat-tcp options (min/max workers)
81
- # FUTURE merge in host options for verbose / keep_alive
82
-
83
- @restart_cmd = RestartCmd.new(@config)
84
- end
85
-
86
- def run
87
- self.run! false
88
- end
89
-
90
- def start
91
- self.run! true
92
- end
93
-
94
- protected
95
-
96
- def run!(daemonize = false)
97
- daemonize!(true) if daemonize && !ENV['SANFORD_SKIP_DAEMONIZE']
98
- Sanford::Server.new(@host, @server_options).tap do |server|
99
- log "Starting #{@host.name} server..."
100
-
101
- server.listen(*@config.listen_args)
102
- $0 = ProcessName.new(@host.name, server.ip, server.port)
103
- log "Listening on #{server.ip}:#{server.port}"
104
-
105
- @config.pid_file.write
106
- log "PID: #{Process.pid}"
107
-
108
- Signal.trap("TERM"){ self.stop!(server) }
109
- Signal.trap("INT"){ self.halt!(server) }
110
- Signal.trap("USR2"){ self.restart!(server) }
111
-
112
- server_thread = server.run(@config.client_file_descriptors)
113
- log "#{@host.name} server started and ready."
114
- server_thread.join
115
- end
116
- rescue RuntimeError => err
117
- log "Error: #{err.message}"
118
- log "#{@host.name} server never started."
119
- ensure
120
- @config.pid_file.remove
121
- end
122
-
123
- def restart!(server)
124
- log "Restarting #{@host.name} server..."
125
- server.pause
126
- log "server paused"
127
-
128
- ENV['SANFORD_HOST'] = @host.name
129
- ENV['SANFORD_SERVER_FD'] = server.file_descriptor.to_s
130
- ENV['SANFORD_CLIENT_FDS'] = server.client_file_descriptors.join(',')
131
- ENV['SANFORD_SKIP_DAEMONIZE'] = 'yes'
132
-
133
- log "calling exec ..."
134
- Dir.chdir @restart_cmd.dir
135
- Kernel.exec(*@restart_cmd.argv)
136
- end
137
-
138
- def stop!(server)
139
- log "Stopping #{@host.name} server..."
140
- server.stop
141
- log "#{@host.name} server stopped."
142
- end
143
-
144
- def halt!(server)
145
- log "Halting #{@host.name} server..."
146
- server.halt false
147
- log "#{@host.name} server halted."
148
- end
149
-
150
- # Full explanation: http://www.steve.org.uk/Reference/Unix/faq_2.html#SEC16
151
- def daemonize!(no_chdir = false, no_close = false)
152
- exit if fork
153
- Process.setsid
154
- exit if fork
155
- Dir.chdir "/" unless no_chdir
156
- if !no_close
157
- null = File.open "/dev/null", 'w'
158
- STDIN.reopen null
159
- STDOUT.reopen null
160
- STDERR.reopen null
161
- end
162
- return 0
163
- end
164
-
165
- def log(message)
166
- @logger.info "[Sanford] #{message}"
167
- end
168
-
169
- end
170
-
171
- class SignalHandler
172
-
173
- def initialize(options = nil)
174
- @config = Config.new(options)
175
- raise Sanford::NoPIDError.new if !@config.pid
176
- end
177
-
178
- def stop
179
- Process.kill("TERM", @config.pid)
180
- end
181
-
182
- def restart
183
- Process.kill("USR2", @config.pid)
184
- end
185
-
186
- end
187
-
188
- class Config
189
- attr_reader :host_name, :host, :ip, :port, :file_descriptor
190
- attr_reader :client_file_descriptors, :pid_file, :pid, :restart_dir
191
-
192
- def initialize(opts = nil)
193
- options = OpenStruct.new(opts || {})
194
- @host_name = ENV['SANFORD_HOST'] || options.host
195
-
196
- @host = @host_name ? Sanford.hosts.find(@host_name) : Sanford.hosts.first
197
- @host ||= NullHost.new
198
-
199
- @file_descriptor = ENV['SANFORD_SERVER_FD'] || options.file_descriptor
200
- @file_descriptor = @file_descriptor.to_i if @file_descriptor
201
- @ip = ENV['SANFORD_IP'] || options.ip || @host.ip
202
- @port = ENV['SANFORD_PORT'] || options.port || @host.port
203
- @port = @port.to_i if @port
204
-
205
- client_fds_str = ENV['SANFORD_CLIENT_FDS'] || options.client_fds || ""
206
- @client_file_descriptors = client_fds_str.split(',').map(&:to_i)
207
-
208
- @pid_file = PIDFile.new(ENV['SANFORD_PID_FILE'] || options.pid_file || @host.pid_file)
209
- @pid = options.pid || @pid_file.pid
210
-
211
- @restart_dir = ENV['SANFORD_RESTART_DIR'] || options.restart_dir
212
- end
213
-
214
- def listen_args
215
- @file_descriptor ? [ @file_descriptor ] : [ @ip, @port ]
216
- end
217
-
218
- def has_listen_args?
219
- !!@file_descriptor || !!(@ip && @port)
220
- end
221
-
222
- def found_host?
223
- !@host.kind_of?(NullHost)
224
- end
225
-
226
- end
227
-
228
- class NullHost
229
- [ :ip, :port, :pid_file ].each do |method_name|
230
- define_method(method_name){ }
231
- end
232
- end
233
-
234
- class ProcessName < String
235
- def initialize(name, ip, port)
236
- super "#{[ name, ip, port ].join('_')}"
237
- end
238
- end
239
-
240
- class PIDFile
241
- DEF_FILE = '/dev/null'
242
-
243
- def initialize(path)
244
- @path = (path || DEF_FILE).to_s
245
- end
246
-
247
- def pid
248
- pid = File.read(@path).strip if File.exists?(@path)
249
- pid.to_i if pid && !pid.empty?
250
- end
251
-
252
- def write
253
- begin
254
- File.open(@path, 'w'){|f| f.puts Process.pid }
255
- rescue Errno::ENOENT => err
256
- e = RuntimeError.new("Can't write pid to file `#{@path}`")
257
- e.set_backtrace(err.backtrace)
258
- raise e
259
- end
260
- end
261
-
262
- def remove
263
- FileUtils.rm_f(@path)
264
- end
265
-
266
- def to_s
267
- @path
268
- end
269
- end
270
-
271
- class RestartCmd
272
- attr_reader :argv, :dir
273
-
274
- def initialize(config = nil)
275
- require 'rubygems'
276
- config ||= OpenStruct.new
277
- @dir = config.restart_dir || get_pwd
278
- @argv = [ Gem.ruby, $0, ARGV.dup ].flatten
279
- end
280
-
281
- protected
282
-
283
- # Trick from puma/unicorn. Favor PWD because it contains an unresolved
284
- # symlink. This is useful when restarting after deploying; the original
285
- # directory may be removed, but the symlink is pointing to a new
286
- # directory.
287
- def get_pwd
288
- env_stat = File.stat(ENV['PWD'])
289
- pwd_stat = File.stat(Dir.pwd)
290
- if env_stat.ino == pwd_stat.ino && env_stat.dev == pwd_stat.dev
291
- ENV['PWD']
292
- else
293
- Dir.pwd
294
- end
295
- end
296
-
297
- end
298
- end
299
-
300
54
  class CLIRB # Version 1.0.0, https://github.com/redding/cli.rb
301
55
  Error = Class.new(RuntimeError);
302
56
  HelpExit = Class.new(RuntimeError); VersionExit = Class.new(RuntimeError)
@@ -352,27 +106,4 @@ module Sanford
352
106
  end
353
107
  end
354
108
 
355
- class NoHostError < CLIRB::Error
356
- def initialize(host_name)
357
- message = if Sanford.hosts.empty?
358
- "No hosts have been defined. Please define a host before trying to run Sanford."
359
- else
360
- "A host couldn't be found with the name #{host_name.inspect}. "
361
- end
362
- super message
363
- end
364
- end
365
-
366
- class InvalidHostError < CLIRB::Error
367
- def initialize(host)
368
- super "A port must be configured or provided to run a server for '#{host}'"
369
- end
370
- end
371
-
372
- class NoPIDError < CLIRB::Error
373
- def initialize
374
- super "A PID or PID file is required"
375
- end
376
- end
377
-
378
109
  end
data/lib/sanford/host.rb CHANGED
@@ -18,16 +18,16 @@ module Sanford
18
18
  # effects (messing up someone's `initialize`). Thus, the `Configuration`
19
19
  # is a separate class and not on the `Host` directly.
20
20
 
21
- option :name, String
22
- option :ip, String, :default => '0.0.0.0'
23
- option :port, Integer
24
- option :pid_file, Pathname
25
- option :logger, :default => proc{ Sanford.config.logger }
26
- option :verbose_logging, :default => true
27
- option :receives_keep_alive, :default => false
28
- option :runner, :default => proc{ Sanford.config.runner }
29
- option :error_procs, Array, :default => []
30
- option :init_proc, Proc, :default => proc{ }
21
+ option :name, String
22
+ option :ip, String, :default => '0.0.0.0'
23
+ option :port, Integer
24
+ option :pid_file, Pathname
25
+ option :logger, :default => proc{ Sanford.config.logger }
26
+ option :verbose_logging, :default => true
27
+ option :receives_keep_alive, :default => false
28
+ option :runner, :default => proc{ Sanford.config.runner }
29
+ option :error_procs, Array, :default => []
30
+ option :init_procs, Array, :default => []
31
31
 
32
32
  def initialize(host)
33
33
  self.name = host.class.to_s
@@ -88,7 +88,7 @@ module Sanford
88
88
  end
89
89
 
90
90
  def init(&block)
91
- self.configuration.init_proc = block
91
+ self.configuration.init_procs << block
92
92
  end
93
93
 
94
94
  def service_handler_ns(value = nil)
@@ -100,7 +100,7 @@ module Sanford
100
100
  if @service_handler_ns && !(handler_class_name =~ /^::/)
101
101
  handler_class_name = "#{@service_handler_ns}::#{handler_class_name}"
102
102
  end
103
- @services[service_name] = handler_class_name
103
+ @services[service_name.to_s] = handler_class_name
104
104
  end
105
105
 
106
106
  def inspect
@@ -109,8 +109,6 @@ module Sanford
109
109
  "port=#{self.configuration.port.inspect}>"
110
110
  end
111
111
 
112
- protected
113
-
114
112
  module ClassMethods
115
113
 
116
114
  # the class level of a `Host` should just proxy it's methods down to it's
@@ -15,7 +15,7 @@ module Sanford
15
15
  attr_reader :name, :logger, :verbose, :keep_alive, :runner, :error_procs
16
16
 
17
17
  def initialize(service_host, options = nil)
18
- service_host.configuration.init_proc.call
18
+ service_host.configuration.init_procs.each(&:call)
19
19
 
20
20
  overrides = self.remove_nil_values(options || {})
21
21
  configuration = service_host.configuration.to_hash.merge(overrides)
@@ -32,14 +32,14 @@ module Sanford
32
32
  end
33
33
  end
34
34
 
35
- def run(handler_class, request)
36
- self.runner.new(handler_class, request, self.logger).run
37
- end
38
-
39
35
  def handler_class_for(service)
40
36
  @handlers[service] || raise(Sanford::NotFoundError)
41
37
  end
42
38
 
39
+ def run(handler_class, request)
40
+ self.runner.new(handler_class, request, self.logger).run
41
+ end
42
+
43
43
  protected
44
44
 
45
45
  def constantize(handler_class_name)
@@ -57,8 +57,8 @@ module Sanford
57
57
 
58
58
  class NoHandlerClassError < RuntimeError
59
59
  def initialize(handler_class_name)
60
- super "Sanford couldn't find the service handler '#{handler_class_name}'. " \
61
- "It doesn't exist or hasn't been required in yet."
60
+ super "Sanford couldn't find the service handler '#{handler_class_name}'."\
61
+ " It doesn't exist or hasn't been required in yet."
62
62
  end
63
63
  end
64
64
 
@@ -0,0 +1,275 @@
1
+ require 'sanford/cli'
2
+ require 'sanford/server'
3
+
4
+ module Sanford
5
+
6
+ module Manager
7
+
8
+ def self.call(action, options = nil)
9
+ get_handler_class(action).new(options).tap{ |manager| manager.send(action) }
10
+ end
11
+
12
+ def self.get_handler_class(action)
13
+ case action.to_sym
14
+ when :start, :run
15
+ ServerHandler
16
+ when :stop, :restart
17
+ SignalHandler
18
+ end
19
+ end
20
+
21
+ class Config
22
+ attr_reader :host_name, :host, :ip, :port, :pid, :pid_file, :restart_dir
23
+ attr_reader :file_descriptor, :client_file_descriptors
24
+
25
+ def initialize(opts = nil)
26
+ options = OpenStruct.new(opts || {})
27
+ @host_name = ENV['SANFORD_HOST'] || options.host
28
+
29
+ @host = @host_name ? Sanford.hosts.find(@host_name) : Sanford.hosts.first
30
+ @host ||= NullHost.new
31
+
32
+ @file_descriptor = ENV['SANFORD_SERVER_FD'] || options.file_descriptor
33
+ @file_descriptor = @file_descriptor.to_i if @file_descriptor
34
+ @ip = ENV['SANFORD_IP'] || options.ip || @host.ip
35
+ @port = ENV['SANFORD_PORT'] || options.port || @host.port
36
+ @port = @port.to_i if @port
37
+
38
+ client_fds_str = ENV['SANFORD_CLIENT_FDS'] || options.client_fds || ""
39
+ @client_file_descriptors = client_fds_str.split(',').map(&:to_i)
40
+
41
+ @pid_file = PIDFile.new(ENV['SANFORD_PID_FILE'] || options.pid_file || @host.pid_file)
42
+ @pid = options.pid || @pid_file.pid
43
+
44
+ @restart_dir = ENV['SANFORD_RESTART_DIR'] || options.restart_dir
45
+ end
46
+
47
+ def listen_args
48
+ @file_descriptor ? [ @file_descriptor ] : [ @ip, @port ]
49
+ end
50
+
51
+ def has_listen_args?
52
+ !!@file_descriptor || !!(@ip && @port)
53
+ end
54
+
55
+ def found_host?
56
+ !@host.kind_of?(NullHost)
57
+ end
58
+
59
+ class NullHost
60
+ [ :ip, :port, :pid_file ].each do |method_name|
61
+ define_method(method_name){ }
62
+ end
63
+ end
64
+
65
+ class PIDFile
66
+ DEF_FILE = '/dev/null'
67
+
68
+ def initialize(path)
69
+ @path = (path || DEF_FILE).to_s
70
+ end
71
+
72
+ def pid
73
+ pid = File.read(@path).strip if File.exists?(@path)
74
+ pid.to_i if pid && !pid.empty?
75
+ end
76
+
77
+ def write
78
+ begin
79
+ File.open(@path, 'w'){|f| f.puts Process.pid }
80
+ rescue Errno::ENOENT => err
81
+ e = RuntimeError.new("Can't write pid to file `#{@path}`")
82
+ e.set_backtrace(err.backtrace)
83
+ raise e
84
+ end
85
+ end
86
+
87
+ def remove
88
+ FileUtils.rm_f(@path)
89
+ end
90
+
91
+ def to_s
92
+ @path
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ class ServerHandler
99
+
100
+ def initialize(options = nil)
101
+ @config = Config.new(options)
102
+ raise Sanford::NoHostError.new(@config.host_name) if !@config.found_host?
103
+ raise Sanford::InvalidHostError.new(@config.host) if !@config.has_listen_args?
104
+ @host = @config.host
105
+ @logger = @host.logger
106
+
107
+ @server_options = {}
108
+ # FUTURE allow passing through dat-tcp options (min/max workers)
109
+ # FUTURE merge in host options for verbose / keep_alive
110
+
111
+ @restart_cmd = RestartCmd.new(@config)
112
+ end
113
+
114
+ def run
115
+ self.run! false
116
+ end
117
+
118
+ def start
119
+ self.run! true
120
+ end
121
+
122
+ protected
123
+
124
+ def run!(daemonize = false)
125
+ daemonize!(true) if daemonize && !ENV['SANFORD_SKIP_DAEMONIZE']
126
+ Sanford::Server.new(@host, @server_options).tap do |server|
127
+ log "Starting #{@host.name} server..."
128
+
129
+ server.listen(*@config.listen_args)
130
+ $0 = ProcessName.new(@host.name, server.ip, server.port)
131
+ log "Listening on #{server.ip}:#{server.port}"
132
+
133
+ @config.pid_file.write
134
+ log "PID: #{Process.pid}"
135
+
136
+ Signal.trap("TERM"){ self.stop!(server) }
137
+ Signal.trap("INT"){ self.halt!(server) }
138
+ Signal.trap("USR2"){ self.restart!(server) }
139
+
140
+ server_thread = server.run(@config.client_file_descriptors)
141
+ log "#{@host.name} server started and ready."
142
+ server_thread.join
143
+ end
144
+ rescue RuntimeError => err
145
+ log "Error: #{err.message}"
146
+ log "#{@host.name} server never started."
147
+ ensure
148
+ @config.pid_file.remove
149
+ end
150
+
151
+ def restart!(server)
152
+ log "Restarting #{@host.name} server..."
153
+ server.pause
154
+ log "server paused"
155
+
156
+ ENV['SANFORD_HOST'] = @host.name
157
+ ENV['SANFORD_SERVER_FD'] = server.file_descriptor.to_s
158
+ ENV['SANFORD_CLIENT_FDS'] = server.client_file_descriptors.join(',')
159
+ ENV['SANFORD_SKIP_DAEMONIZE'] = 'yes'
160
+
161
+ log "calling exec ..."
162
+ Dir.chdir @restart_cmd.dir
163
+ Kernel.exec(*@restart_cmd.argv)
164
+ end
165
+
166
+ def stop!(server)
167
+ log "Stopping #{@host.name} server..."
168
+ server.stop
169
+ log "#{@host.name} server stopped."
170
+ end
171
+
172
+ def halt!(server)
173
+ log "Halting #{@host.name} server..."
174
+ server.halt false
175
+ log "#{@host.name} server halted."
176
+ end
177
+
178
+ # Full explanation: http://www.steve.org.uk/Reference/Unix/faq_2.html#SEC16
179
+ def daemonize!(no_chdir = false, no_close = false)
180
+ exit if fork
181
+ Process.setsid
182
+ exit if fork
183
+ Dir.chdir "/" unless no_chdir
184
+ if !no_close
185
+ null = File.open "/dev/null", 'w'
186
+ STDIN.reopen null
187
+ STDOUT.reopen null
188
+ STDERR.reopen null
189
+ end
190
+ return 0
191
+ end
192
+
193
+ def log(message)
194
+ @logger.info "[Sanford] #{message}"
195
+ end
196
+
197
+ class ProcessName < String
198
+ def initialize(name, ip, port)
199
+ super "#{[ name, ip, port ].join('_')}"
200
+ end
201
+ end
202
+
203
+ class RestartCmd
204
+ attr_reader :argv, :dir
205
+
206
+ def initialize(config = nil)
207
+ require 'rubygems'
208
+ config ||= OpenStruct.new
209
+ @dir = config.restart_dir || get_pwd
210
+ @argv = [ Gem.ruby, $0, ARGV.dup ].flatten
211
+ end
212
+
213
+ protected
214
+
215
+ # Trick from puma/unicorn. Favor PWD because it contains an unresolved
216
+ # symlink. This is useful when restarting after deploying; the original
217
+ # directory may be removed, but the symlink is pointing to a new
218
+ # directory.
219
+ def get_pwd
220
+ env_stat = File.stat(ENV['PWD'])
221
+ pwd_stat = File.stat(Dir.pwd)
222
+ if env_stat.ino == pwd_stat.ino && env_stat.dev == pwd_stat.dev
223
+ ENV['PWD']
224
+ else
225
+ Dir.pwd
226
+ end
227
+ end
228
+
229
+ end
230
+
231
+ end
232
+
233
+ class SignalHandler
234
+
235
+ def initialize(options = nil)
236
+ @config = Config.new(options)
237
+ raise Sanford::NoPIDError.new if !@config.pid
238
+ end
239
+
240
+ def stop
241
+ Process.kill("TERM", @config.pid)
242
+ end
243
+
244
+ def restart
245
+ Process.kill("USR2", @config.pid)
246
+ end
247
+
248
+ end
249
+
250
+ end
251
+
252
+ class NoHostError < CLIRB::Error
253
+ def initialize(host_name)
254
+ message = if Sanford.hosts.empty?
255
+ "No hosts have been defined. Please define a host before trying to run Sanford."
256
+ else
257
+ "A host couldn't be found with the name #{host_name.inspect}. "
258
+ end
259
+ super message
260
+ end
261
+ end
262
+
263
+ class InvalidHostError < CLIRB::Error
264
+ def initialize(host)
265
+ super "A port must be configured or provided to run a server for '#{host}'"
266
+ end
267
+ end
268
+
269
+ class NoPIDError < CLIRB::Error
270
+ def initialize
271
+ super "A PID or PID file is required"
272
+ end
273
+ end
274
+
275
+ end