mongrel2 0.53.0 → 0.54.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +56 -2
- data/History.rdoc +7 -0
- data/Manifest.txt +17 -0
- data/Rakefile +21 -18
- data/bin/m2sh.rb +3 -738
- data/gem.deps.rb +2 -0
- data/lib/mongrel2.rb +1 -1
- data/lib/mongrel2/cli.rb +496 -0
- data/lib/mongrel2/cli/access.rb +29 -0
- data/lib/mongrel2/cli/bootstrap.rb +39 -0
- data/lib/mongrel2/cli/commit.rb +34 -0
- data/lib/mongrel2/cli/hosts.rb +51 -0
- data/lib/mongrel2/cli/init.rb +29 -0
- data/lib/mongrel2/cli/load.rb +41 -0
- data/lib/mongrel2/cli/log.rb +27 -0
- data/lib/mongrel2/cli/quickstart.rb +52 -0
- data/lib/mongrel2/cli/reload.rb +33 -0
- data/lib/mongrel2/cli/routes.rb +51 -0
- data/lib/mongrel2/cli/running.rb +44 -0
- data/lib/mongrel2/cli/servers.rb +34 -0
- data/lib/mongrel2/cli/settings.rb +26 -0
- data/lib/mongrel2/cli/start.rb +72 -0
- data/lib/mongrel2/cli/stop.rb +33 -0
- data/lib/mongrel2/config.rb +1 -0
- data/lib/mongrel2/config/directory.rb +11 -0
- data/lib/mongrel2/config/dsl.rb +13 -20
- data/lib/mongrel2/config/handler.rb +12 -0
- data/lib/mongrel2/config/host.rb +1 -1
- data/lib/mongrel2/config/proxy.rb +6 -0
- data/lib/mongrel2/config/server.rb +3 -2
- data/spec/mongrel2/config/dsl_spec.rb +6 -0
- metadata +73 -14
- metadata.gz.sig +0 -0
data/gem.deps.rb
ADDED
data/lib/mongrel2.rb
CHANGED
data/lib/mongrel2/cli.rb
ADDED
@@ -0,0 +1,496 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'gli'
|
5
|
+
require 'loggability'
|
6
|
+
require 'pastel'
|
7
|
+
require 'pathname'
|
8
|
+
require 'tty/prompt'
|
9
|
+
require 'tty/table'
|
10
|
+
|
11
|
+
require 'mongrel2' unless defined?( Mongrel2 )
|
12
|
+
require 'mongrel2/config'
|
13
|
+
|
14
|
+
|
15
|
+
# A tool for interacting with a Mongrel2 config database and server. This isn't
|
16
|
+
# quite a replacement for the real m2sh yet; here's what I have working so far:
|
17
|
+
#
|
18
|
+
# [√] load Load a config.
|
19
|
+
# [√] config Alias for load.
|
20
|
+
# [-] shell Starts an interactive shell.
|
21
|
+
# [√] access Prints the access log.
|
22
|
+
# [√] servers Lists the servers in a config database.
|
23
|
+
# [√] hosts Lists the hosts in a server.
|
24
|
+
# [√] routes Lists the routes in a host.
|
25
|
+
# [√] commit Adds a message to the log.
|
26
|
+
# [√] log Prints the commit log.
|
27
|
+
# [√] start Starts a server.
|
28
|
+
# [√] stop Stops a server.
|
29
|
+
# [√] reload Reloads a server.
|
30
|
+
# [√] running Tells you what's running.
|
31
|
+
# [-] control Connects to the control port.
|
32
|
+
# [√] version Prints the Mongrel2 and m2sh version.
|
33
|
+
# [√] help Get help, lists commands.
|
34
|
+
# [-] uuid Prints out a randomly generated UUID.
|
35
|
+
#
|
36
|
+
# I just use 'uuidgen' to generate uuids (which is all m2sh does, as
|
37
|
+
# well), so I don't plan to implement that. The 'control' command is more-easily
|
38
|
+
# accessed via pry+Mongrel2::Control, so I'm not going to implement that, either.
|
39
|
+
# Everything else should be analagous to (or better than) the m2sh that comes with
|
40
|
+
# mongrel2. I implemented the 'shell' mode, but I found I never used it, and it
|
41
|
+
# introduced a dependency on the Termios library, so I removed it.
|
42
|
+
#
|
43
|
+
module Mongrel2::CLI
|
44
|
+
extend Loggability,
|
45
|
+
GLI::App
|
46
|
+
|
47
|
+
|
48
|
+
# Write logs to Mongrel2's logger
|
49
|
+
log_to :mongrel2
|
50
|
+
|
51
|
+
|
52
|
+
#
|
53
|
+
# GLI
|
54
|
+
#
|
55
|
+
|
56
|
+
# Set up global[:description] and options
|
57
|
+
program_desc 'Mongrel2 Configurator'
|
58
|
+
|
59
|
+
# The command version
|
60
|
+
version Mongrel2::VERSION
|
61
|
+
|
62
|
+
# Use an OpenStruct for options instead of a Hash
|
63
|
+
use_openstruct( true )
|
64
|
+
|
65
|
+
# Subcommand options are independent of global[:ones]
|
66
|
+
subcommand_option_handling :normal
|
67
|
+
|
68
|
+
# Strict argument validation
|
69
|
+
arguments :strict
|
70
|
+
|
71
|
+
|
72
|
+
# Custom parameter types
|
73
|
+
accept Array do |value|
|
74
|
+
value.strip.split(/\s*,\s*/)
|
75
|
+
end
|
76
|
+
accept Pathname do |value|
|
77
|
+
Pathname( value.strip )
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
# Global options
|
82
|
+
desc 'Enable debugging output'
|
83
|
+
switch [:d, :debug]
|
84
|
+
|
85
|
+
desc 'Enable verbose output'
|
86
|
+
switch [:v, :verbose]
|
87
|
+
|
88
|
+
desc 'Set log level to LEVEL (one of %s)' % [Loggability::LOG_LEVELS.keys.join(', ')]
|
89
|
+
arg_name :LEVEL
|
90
|
+
flag [:l, :loglevel], must_match: Loggability::LOG_LEVELS.keys
|
91
|
+
|
92
|
+
desc "Don't actually do anything, just show what would happen."
|
93
|
+
switch [:n, 'dry-run']
|
94
|
+
|
95
|
+
desc "Additional Ruby libs to require before doing anything."
|
96
|
+
flag [:r, 'requires'], type: Array
|
97
|
+
|
98
|
+
desc "Specify the PATH of the config database to use."
|
99
|
+
arg_name :PATH
|
100
|
+
flag [:config, :C], default: Mongrel2::DEFAULT_CONFIG_URI
|
101
|
+
|
102
|
+
desc "Specify the REASON for an action for the event log."
|
103
|
+
arg_name :REASON
|
104
|
+
flag [:why], type: String
|
105
|
+
|
106
|
+
|
107
|
+
#
|
108
|
+
# GLI Event callbacks
|
109
|
+
#
|
110
|
+
|
111
|
+
# Set up global options
|
112
|
+
pre do |global, command, options, args|
|
113
|
+
self.set_logging_level( global[:l] )
|
114
|
+
Loggability.format_with( :color ) if $stdout.tty?
|
115
|
+
|
116
|
+
# Include a 'lib' directory if there is one
|
117
|
+
$LOAD_PATH.unshift( 'lib' ) if File.directory?( 'lib' )
|
118
|
+
|
119
|
+
self.require_additional_libs( global[:r] ) if global[:r]
|
120
|
+
|
121
|
+
self.setup_pastel_aliases
|
122
|
+
self.setup_output( global )
|
123
|
+
|
124
|
+
Mongrel2::Config.configure( configdb: global.config ) if global.config
|
125
|
+
|
126
|
+
true
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
# Write the error to the log on exceptions.
|
131
|
+
on_error do |exception|
|
132
|
+
|
133
|
+
case exception
|
134
|
+
when OptionParser::ParseError, GLI::CustomExit
|
135
|
+
msg = exception.full_message(highlight: false, order: :bottom)
|
136
|
+
self.log.debug( msg )
|
137
|
+
else
|
138
|
+
msg = exception.full_message(highlight: true, order: :bottom)
|
139
|
+
self.log.error( msg )
|
140
|
+
end
|
141
|
+
|
142
|
+
true
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
|
148
|
+
##
|
149
|
+
# Registered subcommand modules
|
150
|
+
singleton_class.attr_accessor :subcommand_modules
|
151
|
+
|
152
|
+
|
153
|
+
### Overridden -- Add registered subcommands immediately before running.
|
154
|
+
def self::run( * )
|
155
|
+
self.add_registered_subcommands
|
156
|
+
super
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
### Add the specified +mod+ule containing subcommands to the 'mongrel2' command.
|
161
|
+
def self::register_subcommands( mod )
|
162
|
+
self.subcommand_modules ||= []
|
163
|
+
self.subcommand_modules.push( mod )
|
164
|
+
mod.extend( GLI::DSL, GLI::AppSupport, Loggability, CommandUtilities )
|
165
|
+
mod.log_to( :mongrel2 )
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
### Add the commands from the registered subcommand modules.
|
170
|
+
def self::add_registered_subcommands
|
171
|
+
self.subcommand_modules ||= []
|
172
|
+
self.subcommand_modules.each do |mod|
|
173
|
+
merged_commands = mod.commands.merge( self.commands )
|
174
|
+
self.commands.update( merged_commands )
|
175
|
+
command_objs = self.commands_declaration_order | self.commands.values
|
176
|
+
self.commands_declaration_order.replace( command_objs )
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
### Return the Pastel colorizer.
|
182
|
+
###
|
183
|
+
def self::pastel
|
184
|
+
@pastel ||= Pastel.new( enabled: $stdout.tty? )
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
### Return the TTY prompt used by the command to communicate with the
|
189
|
+
### user.
|
190
|
+
def self::prompt
|
191
|
+
@prompt ||= TTY::Prompt.new( output: $stderr )
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
### Discard the existing HighLine prompt object if one existed. Mostly useful for
|
196
|
+
### testing.
|
197
|
+
def self::reset_prompt
|
198
|
+
@prompt = nil
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
### Set the global logging +level+ if it's defined.
|
203
|
+
def self::set_logging_level( level=nil )
|
204
|
+
if level
|
205
|
+
Loggability.level = level.to_sym
|
206
|
+
else
|
207
|
+
Loggability.level = :fatal
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
### Load any additional Ruby libraries given with the -r global option.
|
213
|
+
def self::require_additional_libs( requires)
|
214
|
+
requires.each do |path|
|
215
|
+
path = "mongrel2/#{path}" unless path.start_with?( 'mongrel2/' )
|
216
|
+
require( path )
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
### Setup pastel color aliases
|
222
|
+
###
|
223
|
+
def self::setup_pastel_aliases
|
224
|
+
self.pastel.alias_color( :headline, :bold, :white, :on_black )
|
225
|
+
self.pastel.alias_color( :header, :bold, :white )
|
226
|
+
self.pastel.alias_color( :success, :bold, :green )
|
227
|
+
self.pastel.alias_color( :error, :bold, :red )
|
228
|
+
self.pastel.alias_color( :key, :green )
|
229
|
+
self.pastel.alias_color( :even_row, :bold )
|
230
|
+
self.pastel.alias_color( :odd_row, :reset )
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
### Set up the output levels and globals based on the associated +global+ options.
|
235
|
+
def self::setup_output( global )
|
236
|
+
|
237
|
+
# Turn on Ruby debugging and/or verbosity if specified
|
238
|
+
if global[:n]
|
239
|
+
$DRYRUN = true
|
240
|
+
Loggability.level = :warn
|
241
|
+
else
|
242
|
+
$DRYRUN = false
|
243
|
+
end
|
244
|
+
|
245
|
+
if global[:verbose]
|
246
|
+
$VERBOSE = true
|
247
|
+
Loggability.level = :info
|
248
|
+
end
|
249
|
+
|
250
|
+
if global[:debug]
|
251
|
+
$DEBUG = true
|
252
|
+
Loggability.level = :debug
|
253
|
+
end
|
254
|
+
|
255
|
+
if global[:loglevel]
|
256
|
+
Loggability.level = global[:loglevel]
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|
260
|
+
|
261
|
+
|
262
|
+
#
|
263
|
+
# GLI subcommands
|
264
|
+
#
|
265
|
+
|
266
|
+
|
267
|
+
# Convenience module for subcommand registration syntax sugar.
|
268
|
+
module Subcommand
|
269
|
+
|
270
|
+
### Extension callback -- register the extending object as a subcommand.
|
271
|
+
def self::extended( mod )
|
272
|
+
Mongrel2::CLI.log.debug "Registering subcommands from %p" % [ mod ]
|
273
|
+
Mongrel2::CLI.register_subcommands( mod )
|
274
|
+
end
|
275
|
+
|
276
|
+
|
277
|
+
###############
|
278
|
+
module_function
|
279
|
+
###############
|
280
|
+
|
281
|
+
### Exit with the specified +exit_code+ after printing the given +message+.
|
282
|
+
def exit_now!( message, exit_code=1 )
|
283
|
+
raise GLI::CustomExit.new( message, exit_code )
|
284
|
+
end
|
285
|
+
|
286
|
+
|
287
|
+
### Exit with a helpful +message+ and display the usage.
|
288
|
+
def help_now!( message=nil )
|
289
|
+
exception = OptionParser::ParseError.new( message )
|
290
|
+
def exception.exit_code; 64; end
|
291
|
+
|
292
|
+
raise exception
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
### Get the prompt (a TTY::Prompt object)
|
297
|
+
def prompt
|
298
|
+
return Mongrel2::CLI.prompt
|
299
|
+
end
|
300
|
+
|
301
|
+
|
302
|
+
### Return the global Pastel object for convenient formatting, color, etc.
|
303
|
+
def hl
|
304
|
+
return Mongrel2::CLI.pastel
|
305
|
+
end
|
306
|
+
|
307
|
+
|
308
|
+
### Return the specified +string+ in the 'headline' ANSI color.
|
309
|
+
def headline_string( string )
|
310
|
+
return hl.headline( string )
|
311
|
+
end
|
312
|
+
|
313
|
+
|
314
|
+
### Return the specified +string+ in the 'highlight' ANSI color.
|
315
|
+
def highlight_string( string )
|
316
|
+
return hl.highlight( string )
|
317
|
+
end
|
318
|
+
|
319
|
+
|
320
|
+
### Return the specified +string+ in the 'success' ANSI color.
|
321
|
+
def success_string( string )
|
322
|
+
return hl.success( string )
|
323
|
+
end
|
324
|
+
|
325
|
+
|
326
|
+
### Return the specified +string+ in the 'error' ANSI color.
|
327
|
+
def error_string( string )
|
328
|
+
return hl.error( string )
|
329
|
+
end
|
330
|
+
|
331
|
+
|
332
|
+
### Output a table with the given +header+ (an array) and +rows+
|
333
|
+
### (an array of arrays).
|
334
|
+
def display_table( header, rows )
|
335
|
+
table = TTY::Table.new( header, rows )
|
336
|
+
renderer = nil
|
337
|
+
|
338
|
+
if hl.enabled?
|
339
|
+
renderer = TTY::Table::Renderer::Unicode.new(
|
340
|
+
table,
|
341
|
+
multiline: true,
|
342
|
+
padding: [0,1,0,1]
|
343
|
+
)
|
344
|
+
renderer.border.style = :dim
|
345
|
+
|
346
|
+
else
|
347
|
+
renderer = TTY::Table::Renderer::ASCII.new(
|
348
|
+
table,
|
349
|
+
multiline: true,
|
350
|
+
padding: [0,1,0,1]
|
351
|
+
)
|
352
|
+
end
|
353
|
+
|
354
|
+
puts renderer.render
|
355
|
+
end
|
356
|
+
|
357
|
+
|
358
|
+
### Return the count of visible (i.e., non-control) characters in the given +string+.
|
359
|
+
def visible_chars( string )
|
360
|
+
return string.to_s.gsub(/\e\[.*?m/, '').scan( /\P{Cntrl}/ ).size
|
361
|
+
end
|
362
|
+
|
363
|
+
|
364
|
+
### In dry-run mode, output the description instead of running the provided block and
|
365
|
+
### return the +return_value+.
|
366
|
+
### If dry-run mode is not enabled, yield to the block.
|
367
|
+
def unless_dryrun( description, return_value=true )
|
368
|
+
if $DRYRUN
|
369
|
+
self.log.warn( "DRYRUN> #{description}" )
|
370
|
+
return return_value
|
371
|
+
else
|
372
|
+
return yield
|
373
|
+
end
|
374
|
+
end
|
375
|
+
alias_method :unless_dry_run, :unless_dryrun
|
376
|
+
|
377
|
+
end # module Subcommand
|
378
|
+
|
379
|
+
|
380
|
+
# Functions for common command tasks
|
381
|
+
module CommandUtilities
|
382
|
+
|
383
|
+
### Search the current mongrel2 config for a server matching +serverspec+ and
|
384
|
+
### return it as a Mongrel2::Config::Server object.
|
385
|
+
def find_server( serverspec=nil )
|
386
|
+
server = nil
|
387
|
+
servers = Mongrel2::Config.servers
|
388
|
+
|
389
|
+
raise "No servers are configured." if servers.empty?
|
390
|
+
|
391
|
+
# If there's only one configured server, just make sure if a serverspec was given
|
392
|
+
# that it would have matched.
|
393
|
+
if servers.length == 1
|
394
|
+
server = servers.first if !serverspec ||
|
395
|
+
servers.first.values.values_at( :uuid, :default_host, :name ).include?( serverspec )
|
396
|
+
|
397
|
+
# Otherwise, require an argument and search for the desired server if there is one
|
398
|
+
else
|
399
|
+
raise "You must specify a server uuid/hostname/name when more " +
|
400
|
+
"than one server is configured." if servers.length > 1 && !serverspec
|
401
|
+
|
402
|
+
server = servers.find {|s| s.uuid == serverspec } ||
|
403
|
+
servers.find {|s| s.default_host == serverspec } ||
|
404
|
+
servers.find {|s| s.name == serverspec }
|
405
|
+
end
|
406
|
+
|
407
|
+
raise "No servers match '#{serverspec}'" unless server
|
408
|
+
|
409
|
+
return server
|
410
|
+
end
|
411
|
+
|
412
|
+
|
413
|
+
### Read command line history from HISTORY_FILE
|
414
|
+
def read_history
|
415
|
+
histfile = HISTORY_FILE.expand_path
|
416
|
+
|
417
|
+
if histfile.exist?
|
418
|
+
lines = histfile.readlines.collect {|line| line.chomp }
|
419
|
+
self.log.debug "Read %d saved history commands from %s." % [ lines.length, histfile ]
|
420
|
+
Readline::HISTORY.push( *lines )
|
421
|
+
else
|
422
|
+
self.log.debug "History file '%s' was empty or non-existant." % [ histfile ]
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
|
427
|
+
### Save command line history to HISTORY_FILE
|
428
|
+
def save_history
|
429
|
+
histfile = HISTORY_FILE.expand_path
|
430
|
+
|
431
|
+
lines = Readline::HISTORY.to_a.reverse.uniq.reverse
|
432
|
+
lines = lines[ -DEFAULT_HISTORY_SIZE, DEFAULT_HISTORY_SIZE ] if
|
433
|
+
lines.length > DEFAULT_HISTORY_SIZE
|
434
|
+
|
435
|
+
self.log.debug "Saving %d history lines to %s." % [ lines.length, histfile ]
|
436
|
+
|
437
|
+
histfile.open( File::WRONLY|File::CREAT|File::TRUNC ) do |ofh|
|
438
|
+
ofh.puts( *lines )
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
|
443
|
+
### Invoke the user's editor on the given +filename+ and return the exit code
|
444
|
+
### from doing so.
|
445
|
+
def edit( filename )
|
446
|
+
editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR
|
447
|
+
system editor, filename.to_s
|
448
|
+
unless $?.success? || editor =~ /vim/i
|
449
|
+
raise "Editor exited with an error status (%d)" % [ $?.exitstatus ]
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
|
454
|
+
### Search the PATH for a mongrel2 binary, returning the absolute Pathname to it if found, and
|
455
|
+
### outputting a warning and describing how to set ENV['MONGREL2'] if not.
|
456
|
+
def find_mongrel2
|
457
|
+
if ENV['MONGREL2']
|
458
|
+
m2 = Pathname( ENV['MONGREL2'] )
|
459
|
+
error = nil
|
460
|
+
if !m2.file?
|
461
|
+
error = "but it isn't a plain file."
|
462
|
+
elsif !m2.executable?
|
463
|
+
error = "but it isn't executable."
|
464
|
+
end
|
465
|
+
|
466
|
+
raise "MONGREL2 was set to #{m2}, #{error}" if error
|
467
|
+
|
468
|
+
return m2
|
469
|
+
else
|
470
|
+
m2 = ENV['PATH'].split( File::PATH_SEPARATOR ).
|
471
|
+
map {|dir| Pathname(dir) + 'mongrel2' }.
|
472
|
+
find {|path| path.executable? }
|
473
|
+
|
474
|
+
return m2 if m2
|
475
|
+
|
476
|
+
raise "The 'mongrel2' binary doesn't seem to be in your PATH. Either " +
|
477
|
+
"add the appropriate directory to your PATH or set the MONGREL2 " +
|
478
|
+
"environment variable to the full path."
|
479
|
+
end
|
480
|
+
|
481
|
+
end
|
482
|
+
|
483
|
+
end # module CommandUtilities
|
484
|
+
|
485
|
+
### Load commands from any files in the specified directory relative to LOAD_PATHs
|
486
|
+
def self::commands_from( subdir )
|
487
|
+
Gem.find_latest_files( File.join(subdir, '*.rb') ).each do |rbfile|
|
488
|
+
self.log.debug " loading %s..." % [ rbfile ]
|
489
|
+
require( rbfile )
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
|
494
|
+
commands_from 'mongrel2/cli'
|
495
|
+
|
496
|
+
end # class Mongrel2::CLI
|