-
Notifications
You must be signed in to change notification settings - Fork 217
/
runner.rb
242 lines (194 loc) · 7.81 KB
/
runner.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
require 'goliath/goliath'
require 'goliath/server'
require 'optparse'
require 'log4r'
module Goliath
# The Goliath::Runner is responsible for parsing any provided options, settting up the
# rack application, creating a logger, and then executing the Goliath::Server with the loaded information.
class Runner
# The address of the server @example 127.0.0.1
# @return [String] The server address
attr_accessor :address
# The port of the server @example 9000
# @return [Integer] The server port
attr_accessor :port
# Flag to determine if the server should daemonize
# @return [Boolean] True if the server should daemonize, false otherwise
attr_accessor :daemonize
# Flag to determine if the server should run in verbose mode
# @return [Boolean] True to turn on verbose mode, false otherwise
attr_accessor :verbose
# Flag to determine if the server should log to standard output
# @return [Boolean] True if the server should log to stdout, false otherwise
attr_accessor :log_stdout
# The log file for the server
# @return [String] The file the server should log too
attr_accessor :log_file
# The pid file for the server
# @return [String] The file to write the servers pid file into
attr_accessor :pid_file
# The Rack application
# @return [Object] The rack application the server will execute
attr_accessor :app
# The API application
# @return [Object] The API application the server will execute
attr_accessor :api
# The plugins the server will execute
# @return [Array] The list of plugins to be executed by the server
attr_accessor :plugins
# Any additional server options
# @return [Hash] Any options to be passed to the server
attr_accessor :app_options
# The parsed options
# @return [Hash] The options parsed by the runner
attr_reader :options
# Create a new Goliath::Runner
#
# @param argv [Array] The command line arguments
# @param api [Object | nil] The Goliath::API this runner is for, can be nil
# @return [Goliath::Runner] An initialized Goliath::Runner
def initialize(argv, api)
api.options_parser(options_parser, options) if api
options_parser.parse!(argv)
Goliath.env = options.delete(:env)
@api = api
@address = options.delete(:address)
@port = options.delete(:port)
@log_file = options.delete(:log_file)
@pid_file = options.delete(:pid_file)
@log_stdout = options.delete(:log_stdout)
@daemonize = options.delete(:daemonize)
@verbose = options.delete(:verbose)
@server_options = options
end
# Create the options parser
#
# @return [OptionParser] Creates the options parser for the runner with the default options
def options_parser
@options ||= {
:address => Goliath::Server::DEFAULT_ADDRESS,
:port => Goliath::Server::DEFAULT_PORT,
:daemonize => false,
:verbose => false,
:log_stdout => false,
:env => :development,
}
@options_parser ||= OptionParser.new do |opts|
opts.banner = "Usage: <server> [options]"
opts.separator ""
opts.separator "Server options:"
opts.on('-e', '--environment NAME', "Set the execution environment (prod, dev or test) (default: #{@options[:env]})") { |val| @options[:env] = val }
opts.on('-a', '--address HOST', "Bind to HOST address (default: #{@options[:address]})") { |addr| @options[:address] = addr }
opts.on('-p', '--port PORT', "Use PORT (default: #{@options[:port]})") { |port| @options[:port] = port.to_i }
opts.on('-u', '--user USER', "Run as specified user") {|v| @options[:user] = v }
opts.on('-l', '--log FILE', "Log to file (default: off)") { |file| @options[:log_file] = file }
opts.on('-s', '--stdout', "Log to stdout (default: #{@options[:log_stdout]})") { |v| @options[:log_stdout] = v }
opts.on('-c', '--config FILE', "Config file (default: ./config/<server>.rb)") { |v| @options[:config] = v }
opts.on('-P', '--pid FILE', "Pid file (default: off)") { |file| @options[:pid_file] = file }
opts.on('-d', '--daemonize', "Run daemonized in the background (default: #{@options[:daemonize]})") { |v| @options[:daemonize] = v }
opts.on('-v', '--verbose', "Enable verbose logging (default: #{@options[:verbose]})") { |v| @options[:verbose] = v }
opts.on('-h', '--help', 'Display help message') { show_options(opts) }
end
end
# Stores the list of plugins to be used by the server
#
# @param plugins [Array] The list of plugins to use
# @return [Nil]
def load_plugins(plugins)
@plugins = plugins
end
# Create environment to run the server.
# If daemonize is set this will fork off a child and kill the runner.
#
# @return [Nil]
def run
unless Goliath.test?
$LOADED_FEATURES.unshift(File.basename($0))
Dir.chdir(File.expand_path(File.dirname($0)))
end
if @daemonize
Process.fork do
Process.setsid
exit if fork
@pid_file ||= './goliath.pid'
@log_file ||= File.expand_path('goliath.log')
store_pid(Process.pid)
File.umask(0000)
stdout_log_file = "#{File.dirname(@log_file)}/#{File.basename(@log_file)}_stdout.log"
STDIN.reopen("/dev/null")
STDOUT.reopen(stdout_log_file, "a")
STDERR.reopen(STDOUT)
run_server
end
else
run_server
end
end
private
# Output the servers options
#
# @param opts [OptionsParser] The options parser
# @return [exit] This will exit the server
def show_options(opts)
puts opts
at_exit { exit! }
exit
end
# Sets up the logging for the runner
# @return [Logger] The logger object
def setup_logger
log = Log4r::Logger.new('goliath')
log.level = @verbose ? Log4r::DEBUG : Log4r::INFO
# allow api to setup logging as it desires
if api.respond_to?(:setup_logger)
api.setup_logger(log, options)
return log
end
log_format = Log4r::PatternFormatter.new(:pattern => "[#{Process.pid}:%l] %d :: %m")
setup_file_logger(log, log_format) if @log_file
setup_stdout_logger(log, log_format) if @log_stdout
log
end
# Setup file logging
#
# @param log [Logger] The logger to add file logging too
# @param log_format [Log4r::Formatter] The log format to use
# @return [Nil]
def setup_file_logger(log, log_format)
FileUtils.mkdir_p(File.dirname(@log_file))
log.add(Log4r::FileOutputter.new('fileOutput', {:filename => @log_file,
:trunc => false,
:formatter => log_format}))
end
# Setup stdout logging
#
# @param log [Logger] The logger to add stdout logging too
# @param log_format [Log4r::Formatter] The log format to use
# @return [Nil]
def setup_stdout_logger(log, log_format)
log.add(Log4r::StdoutOutputter.new('console', :formatter => log_format))
end
# Run the server
#
# @return [Nil]
def run_server
log = setup_logger
log.info("Starting server on #{@address}:#{@port} in #{Goliath.env} mode. Watch out for stones.")
server = Goliath::Server.new(@address, @port)
server.logger = log
server.app = @app
server.api = @api
server.plugins = @plugins || []
server.options = @server_options
server.start
end
# Store the servers pid into the @pid_file
#
# @param pid [Integer] The pid to store
# @return [Nil]
def store_pid(pid)
FileUtils.mkdir_p(File.dirname(@pid_file))
File.open(@pid_file, 'w') { |f| f.write(pid) }
end
end
end