nginx_stage 0.3.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 +7 -0
- data/.gitignore +14 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +199 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +19 -0
- data/Rakefile +36 -0
- data/bin/node +15 -0
- data/bin/ood_ruby +38 -0
- data/bin/python +15 -0
- data/bin/ruby +15 -0
- data/lib/nginx_stage/application.rb +125 -0
- data/lib/nginx_stage/configuration.rb +418 -0
- data/lib/nginx_stage/errors.rb +46 -0
- data/lib/nginx_stage/generator.rb +139 -0
- data/lib/nginx_stage/generator_helpers.rb +68 -0
- data/lib/nginx_stage/generators/app_clean_generator.rb +38 -0
- data/lib/nginx_stage/generators/app_config_generator.rb +101 -0
- data/lib/nginx_stage/generators/app_list_generator.rb +24 -0
- data/lib/nginx_stage/generators/app_reset_generator.rb +54 -0
- data/lib/nginx_stage/generators/nginx_clean_generator.rb +61 -0
- data/lib/nginx_stage/generators/nginx_list_generator.rb +22 -0
- data/lib/nginx_stage/generators/nginx_process_generator.rb +47 -0
- data/lib/nginx_stage/generators/nginx_show_generator.rb +48 -0
- data/lib/nginx_stage/generators/pun_config_generator.rb +102 -0
- data/lib/nginx_stage/pid_file.rb +37 -0
- data/lib/nginx_stage/socket_file.rb +51 -0
- data/lib/nginx_stage/user.rb +75 -0
- data/lib/nginx_stage/version.rb +4 -0
- data/lib/nginx_stage/views/app_config_view.rb +42 -0
- data/lib/nginx_stage/views/pun_config_view.rb +144 -0
- data/lib/nginx_stage.rb +133 -0
- data/nginx_stage.gemspec +24 -0
- data/sbin/nginx_stage +7 -0
- data/share/nginx_stage_example.yml +166 -0
- data/templates/app.conf.erb +14 -0
- data/templates/pun.conf.erb +79 -0
- data/test/minitest_helper.rb +4 -0
- data/test/test_nginx_stage.rb +11 -0
- metadata +132 -0
@@ -0,0 +1,418 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module NginxStage
|
4
|
+
# An object that stores the configuration options to control NginxStage's
|
5
|
+
# behavior.
|
6
|
+
module Configuration
|
7
|
+
#
|
8
|
+
# Global
|
9
|
+
#
|
10
|
+
|
11
|
+
# Location of ERB templates used as NGINX configs
|
12
|
+
# @return [String] the ERB templates root path
|
13
|
+
attr_accessor :template_root
|
14
|
+
|
15
|
+
#
|
16
|
+
# NGINX-specific configuration options
|
17
|
+
#
|
18
|
+
|
19
|
+
# The reverse proxy daemon user used to access the sockets
|
20
|
+
# @return [String] the reverse-proxy-daemon user
|
21
|
+
attr_accessor :proxy_user
|
22
|
+
|
23
|
+
# Path to system-installed NGINX binary
|
24
|
+
# @return [String] the system-installed NGINX binary
|
25
|
+
attr_accessor :nginx_bin
|
26
|
+
|
27
|
+
# A whitelist of signals that can be sent to the NGINX process
|
28
|
+
# @return [Array<Symbol>] whitelist of NGINX process signals
|
29
|
+
attr_accessor :nginx_signals
|
30
|
+
|
31
|
+
# Path to system-installed NGINX mime.types config file
|
32
|
+
# @return [String] the system-installed NGINX mime.types config
|
33
|
+
attr_accessor :mime_types_path
|
34
|
+
|
35
|
+
# Path to system-installed Passenger locations.ini file
|
36
|
+
# @return [String] the system-installed Passenger locations.ini
|
37
|
+
attr_accessor :passenger_root
|
38
|
+
|
39
|
+
# Path to system-installed Ruby binary
|
40
|
+
# @return [String] the system-installed Ruby binary
|
41
|
+
attr_accessor :passenger_ruby
|
42
|
+
|
43
|
+
# Path to system-installed NodeJS binary
|
44
|
+
# @return [String] the system-installed NodeJS binary
|
45
|
+
attr_accessor :passenger_nodejs
|
46
|
+
|
47
|
+
# Path to system-installed python binary
|
48
|
+
# @return [String] the system-installed python binary
|
49
|
+
attr_accessor :passenger_python
|
50
|
+
|
51
|
+
#
|
52
|
+
# per-user NGINX configuration options
|
53
|
+
#
|
54
|
+
|
55
|
+
# Root location where per-user NGINX configs are generated
|
56
|
+
# Path to generated per-user NGINX config file
|
57
|
+
# @example User Bob's nginx config
|
58
|
+
# pun_config_path(user: 'bob')
|
59
|
+
# #=> "/var/log/nginx/config/puns/bob.conf"
|
60
|
+
# @param user [String] the user of the nginx process
|
61
|
+
# @return [String] the path to the per-user nginx config file
|
62
|
+
def pun_config_path(user:)
|
63
|
+
File.expand_path @pun_config_path % {user: user}
|
64
|
+
end
|
65
|
+
|
66
|
+
attr_writer :pun_config_path
|
67
|
+
|
68
|
+
# Path to user's personal tmp root
|
69
|
+
# @example User Bob's nginx tmp root
|
70
|
+
# pun_tmp_root(user: 'bob')
|
71
|
+
# #=> "/var/lib/nginx/tmp/bob"
|
72
|
+
# @param user [String] the user of the nginx process
|
73
|
+
# @return [String] the path to the tmp root
|
74
|
+
def pun_tmp_root(user:)
|
75
|
+
File.expand_path @pun_tmp_root % {user: user}
|
76
|
+
end
|
77
|
+
|
78
|
+
attr_writer :pun_tmp_root
|
79
|
+
|
80
|
+
# Path to the user's personal access.log
|
81
|
+
# @example User Bob's nginx access log
|
82
|
+
# pun_access_log_path(user: 'bob')
|
83
|
+
# #=> "/var/log/nginx/bob/access.log"
|
84
|
+
# @param user [String] the user of the nginx process
|
85
|
+
# @return [String] the path to the nginx access log
|
86
|
+
def pun_access_log_path(user:)
|
87
|
+
File.expand_path @pun_access_log_path % {user: user}
|
88
|
+
end
|
89
|
+
|
90
|
+
attr_writer :pun_access_log_path
|
91
|
+
|
92
|
+
# Path to the user's personal error.log
|
93
|
+
# @example User Bob's nginx error log
|
94
|
+
# pun_error_log_path(user: 'bob')
|
95
|
+
# #=> "/var/log/nginx/bob/error.log"
|
96
|
+
# @param user [String] the user of the nginx process
|
97
|
+
# @return [String] the path to the nginx error log
|
98
|
+
def pun_error_log_path(user:)
|
99
|
+
File.expand_path @pun_error_log_path % {user: user}
|
100
|
+
end
|
101
|
+
|
102
|
+
attr_writer :pun_error_log_path
|
103
|
+
|
104
|
+
# Path to the user's per-user NGINX pid file
|
105
|
+
# @example User Bob's pid file
|
106
|
+
# pun_pid_path(user: 'bob')
|
107
|
+
# #=> "/var/run/nginx/bob/passenger.pid"
|
108
|
+
# @param user [String] the user of nginx process
|
109
|
+
# @return [String] the path to the pid file
|
110
|
+
def pun_pid_path(user:)
|
111
|
+
File.expand_path @pun_pid_path % {user: user}
|
112
|
+
end
|
113
|
+
|
114
|
+
attr_writer :pun_pid_path
|
115
|
+
|
116
|
+
# Path to the user's per-user NGINX socket file
|
117
|
+
# @example User Bob's socket file
|
118
|
+
# socket_path(user: 'bob')
|
119
|
+
# #=> "/var/run/nginx/bob/passenger.sock"
|
120
|
+
# @param user [String] the user of nginx process
|
121
|
+
# @return [String] the path to the socket file
|
122
|
+
def pun_socket_path(user:)
|
123
|
+
File.expand_path @pun_socket_path % {user: user}
|
124
|
+
end
|
125
|
+
|
126
|
+
attr_writer :pun_socket_path
|
127
|
+
|
128
|
+
# Path to the local filesystem root where the per-user Nginx serves files
|
129
|
+
# from with the sendfile feature
|
130
|
+
# @example Filesystem root for user Bob
|
131
|
+
# pun_sendfile_root(user: 'bob')
|
132
|
+
# #=> "/"
|
133
|
+
# @param user [String] the user of the nginx process
|
134
|
+
# @return [String] the path to the filesystem root that is served
|
135
|
+
def pun_sendfile_root(user:)
|
136
|
+
File.expand_path @pun_sendfile_root % {user: user}
|
137
|
+
end
|
138
|
+
|
139
|
+
attr_writer :pun_sendfile_root
|
140
|
+
|
141
|
+
# The internal URI used to access the filesystem for downloading files from
|
142
|
+
# the browser, not including any base-uri
|
143
|
+
# @example
|
144
|
+
# pun_sendfile_uri
|
145
|
+
# #=> "/sendfile"
|
146
|
+
# @return [String] the internal URI used to access filesystem
|
147
|
+
attr_accessor :pun_sendfile_uri
|
148
|
+
|
149
|
+
# List of hashes that help define the location of the app configs for the
|
150
|
+
# per-user NGINX config. These will be arguments for {#app_config_path}.
|
151
|
+
# @example User Bob's app configs
|
152
|
+
# pun_app_configs(user: 'bob')
|
153
|
+
# #=> [ {env: :dev, owner: 'bob', name: '*'},
|
154
|
+
# {env: :usr, owner: '*', name: '*'} ]
|
155
|
+
# @param user [String] the user of the nginx process
|
156
|
+
# @return [Array<Hash>] list of hashes detailing app config locations
|
157
|
+
def pun_app_configs(user:)
|
158
|
+
@pun_app_configs.map do |envmt|
|
159
|
+
envmt.each_with_object({}) do |(k, v), h|
|
160
|
+
h[k] = v.respond_to?(:%) ? (v % {user: user}) : v
|
161
|
+
h[k] = v.to_sym if k == :env
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
attr_writer :pun_app_configs
|
167
|
+
|
168
|
+
#
|
169
|
+
# per-user NGINX app configuration options
|
170
|
+
#
|
171
|
+
|
172
|
+
# Path to generated NGINX app config
|
173
|
+
# @example Dev app owned by Bob
|
174
|
+
# app_config_path(env: :dev, owner: 'bob', name: 'rails1')
|
175
|
+
# #=> "/var/lib/nginx/config/apps/dev/bob/rails1.conf"
|
176
|
+
# @example User app owned by Dan
|
177
|
+
# app_config_path(env: :usr, owner: 'dan', name: 'fillsim')
|
178
|
+
# #=> "/var/lib/nginx/config/apps/usr/dan/fillsim.conf"
|
179
|
+
# @param env [Symbol] environment the app is run under
|
180
|
+
# @param owner [String] the owner of the app
|
181
|
+
# @param name [String] the name of the app
|
182
|
+
# @return [String] the path to the nginx app config on the local filesystem
|
183
|
+
def app_config_path(env:, owner:, name:)
|
184
|
+
File.expand_path @app_config_path[env] % {env: env, owner: owner, name: name}
|
185
|
+
end
|
186
|
+
|
187
|
+
attr_writer :app_config_path
|
188
|
+
|
189
|
+
# Path to the app root on the local filesystem
|
190
|
+
# @example App root for dev app owned by Bob
|
191
|
+
# app_root(env: :dev, owner: 'bob', name: 'rails1')
|
192
|
+
# #=> "~bob/ondemand/dev/rails1"
|
193
|
+
# @example App root for user app owned by Dan
|
194
|
+
# app_root(env: :usr, owner: 'dan', name: 'fillsim')
|
195
|
+
# #=> "/var/www/ood/apps/usr/dan/gateway/fillsim"
|
196
|
+
# @param env [Symbol] environment the app is run under
|
197
|
+
# @param owner [String] the owner of the app
|
198
|
+
# @param name [String] the name of the app
|
199
|
+
# @return [String] the path to the app root on the local filesystem
|
200
|
+
# @raise [InvalidRequest] if the environment specified doesn't exist
|
201
|
+
def app_root(env:, owner:, name:)
|
202
|
+
File.expand_path(
|
203
|
+
@app_root.fetch(env) do
|
204
|
+
raise InvalidRequest, "invalid request environment: #{env}"
|
205
|
+
end % {env: env, owner: owner, name: name}
|
206
|
+
)
|
207
|
+
end
|
208
|
+
|
209
|
+
attr_writer :app_root
|
210
|
+
|
211
|
+
# The URI used to access the app from the browser, not including any base-uri
|
212
|
+
# @example URI for dev app owned by Bob
|
213
|
+
# app_request_uri(env: :dev, owner: 'bob', name: 'rails1')
|
214
|
+
# #=> "/dev/rails1"
|
215
|
+
# @example URI for user app owned by Dan
|
216
|
+
# app_request_uri(env: :dev, owner: 'dan', name: 'fillsim')
|
217
|
+
# #=> "/usr/dan/fillsim"
|
218
|
+
# @param env [Symbol] environment the app is run under
|
219
|
+
# @param owner [String] the owner of the app
|
220
|
+
# @param name [String] the name of the app
|
221
|
+
# @return [String] the URI used to access a given app
|
222
|
+
# @raise [InvalidRequest] if the environment specified doesn't exist
|
223
|
+
def app_request_uri(env:, owner:, name:)
|
224
|
+
@app_request_uri.fetch(env) do
|
225
|
+
raise InvalidRequest, "invalid request environment: #{env}"
|
226
|
+
end % {env: env, owner: owner, name: name}
|
227
|
+
end
|
228
|
+
|
229
|
+
attr_writer :app_request_uri
|
230
|
+
|
231
|
+
# Regular expression used to distinguish the environment from a request URI
|
232
|
+
# and from there distinguish the app owner and app name
|
233
|
+
# @see #app_request_uri
|
234
|
+
# @return [Hash] hash of regular expressions used to determine app from app
|
235
|
+
# namespace for given environment
|
236
|
+
def app_request_regex
|
237
|
+
@app_request_regex.each_with_object({}) { |(k, v), h| h[k] = ::Regexp.new v }
|
238
|
+
end
|
239
|
+
|
240
|
+
attr_writer :app_request_regex
|
241
|
+
|
242
|
+
# Token used to identify a given app from the other apps
|
243
|
+
# @example app token for dev app owned by Bob
|
244
|
+
# app_token(env: :dev, owner: 'bob', name: 'rails1')
|
245
|
+
# #=> "dev/bob/rails1"
|
246
|
+
# @example app token for user app owned by Dan
|
247
|
+
# app_request_uri(env: :dev, owner: 'dan', name: 'fillsim')
|
248
|
+
# #=> "usr/dan/fillsim"
|
249
|
+
# @param env [Symbol] environment the app is run under
|
250
|
+
# @param owner [String] the owner of the app
|
251
|
+
# @param name [String] the name of the app
|
252
|
+
# @return [String] the token identifying the app
|
253
|
+
# @raise [InvalidRequest] if the environment specified doesn't exist
|
254
|
+
def app_token(env:, owner:, name:)
|
255
|
+
@app_token.fetch(env) do
|
256
|
+
raise InvalidRequest, "invalid request environment: #{env}"
|
257
|
+
end % {env: env, owner: owner, name: name}
|
258
|
+
end
|
259
|
+
|
260
|
+
attr_writer :app_token
|
261
|
+
|
262
|
+
# The passenger environment used for the given app environment
|
263
|
+
# @example Dev app owned by Bob
|
264
|
+
# app_passenger_env(env: :dev, owner: 'bob', name: 'rails1')
|
265
|
+
# #=> "development"
|
266
|
+
# @example User app owned by Dan
|
267
|
+
# app_passenger_env(env: :usr, owner: 'dan', name: 'fillsim')
|
268
|
+
# #=> "production"
|
269
|
+
# @param env [Symbol] environment the app is run under
|
270
|
+
# @param owner [String] the owner of the app
|
271
|
+
# @param name [String] the name of the app
|
272
|
+
# @return [String] the passenger environment to run app with in PUN
|
273
|
+
def app_passenger_env(env:, owner:, name:)
|
274
|
+
@app_passenger_env.fetch(env, "production")
|
275
|
+
end
|
276
|
+
|
277
|
+
attr_writer :app_passenger_env
|
278
|
+
|
279
|
+
#
|
280
|
+
# Validation configuration options
|
281
|
+
#
|
282
|
+
|
283
|
+
# Regular expression used to validate a given user name
|
284
|
+
# @return [Regexp] user name regular expression
|
285
|
+
def user_regex
|
286
|
+
/\A#{@user_regex}\z/
|
287
|
+
end
|
288
|
+
|
289
|
+
attr_writer :user_regex
|
290
|
+
|
291
|
+
# Minimum user id required to run the per-user NGINX as this user. This
|
292
|
+
# restricts running processes as special users (i.e., 'root')
|
293
|
+
# @return [Integer] minimum user id required to run as user
|
294
|
+
attr_accessor :min_uid
|
295
|
+
|
296
|
+
# Restrict starting up per-user NGINX process as user with this shell.
|
297
|
+
# NB: This only affects the <tt>pun</tt> command, you are still able to
|
298
|
+
# start or stop the PUN using other commands (e.g., <tt>nginx</tt>,
|
299
|
+
# <tt>nginx_clean</tt>, ...)
|
300
|
+
# @return [String] user shell that is blocked
|
301
|
+
attr_accessor :disabled_shell
|
302
|
+
|
303
|
+
#
|
304
|
+
# Configuration module
|
305
|
+
#
|
306
|
+
|
307
|
+
# Default configuration file
|
308
|
+
# @return [String] path to default yaml configuration file
|
309
|
+
def default_config_path
|
310
|
+
config = config_file
|
311
|
+
unless File.file?(config)
|
312
|
+
config = File.join root, 'config', 'nginx_stage.yml'
|
313
|
+
warn "[DEPRECATION] The file '#{config}' is being deprecated. Please move this file to '#{config_file}'." if File.file?(config)
|
314
|
+
end
|
315
|
+
config
|
316
|
+
end
|
317
|
+
|
318
|
+
# Yields the configuration object.
|
319
|
+
# @yieldparam [Configuration] config The library configuration
|
320
|
+
# @return [void]
|
321
|
+
def configure
|
322
|
+
yield self
|
323
|
+
end
|
324
|
+
|
325
|
+
# Sets default configuration options in any class that extends {Configuration}
|
326
|
+
def self.extended(base)
|
327
|
+
base.set_default_configuration
|
328
|
+
end
|
329
|
+
|
330
|
+
# Sets the default configuration options
|
331
|
+
# @return [void]
|
332
|
+
def set_default_configuration
|
333
|
+
self.template_root = "#{root}/templates"
|
334
|
+
|
335
|
+
self.proxy_user = 'apache'
|
336
|
+
self.nginx_bin = '/opt/rh/nginx16/root/usr/sbin/nginx'
|
337
|
+
self.nginx_signals = %i(stop quit reopen reload)
|
338
|
+
self.mime_types_path = '/opt/rh/nginx16/root/etc/nginx/mime.types'
|
339
|
+
self.passenger_root = '/opt/rh/rh-passenger40/root/usr/share/passenger/phusion_passenger/locations.ini'
|
340
|
+
self.passenger_ruby = "#{root}/bin/ruby"
|
341
|
+
self.passenger_nodejs = "#{root}/bin/node"
|
342
|
+
self.passenger_python = "#{root}/bin/python"
|
343
|
+
|
344
|
+
self.pun_config_path = '/var/lib/nginx/config/puns/%{user}.conf'
|
345
|
+
self.pun_tmp_root = '/var/lib/nginx/tmp/%{user}'
|
346
|
+
self.pun_access_log_path = '/var/log/nginx/%{user}/access.log'
|
347
|
+
self.pun_error_log_path = '/var/log/nginx/%{user}/error.log'
|
348
|
+
self.pun_pid_path = '/var/run/nginx/%{user}/passenger.pid'
|
349
|
+
self.pun_socket_path = '/var/run/nginx/%{user}/passenger.sock'
|
350
|
+
self.pun_sendfile_root = '/'
|
351
|
+
self.pun_sendfile_uri = '/sendfile'
|
352
|
+
self.pun_app_configs = [
|
353
|
+
{env: :dev, owner: '%{user}', name: '*'},
|
354
|
+
{env: :usr, owner: '*', name: '*'},
|
355
|
+
{env: :sys, owner: '', name: '*'}
|
356
|
+
]
|
357
|
+
|
358
|
+
self.app_config_path = {
|
359
|
+
dev: '/var/lib/nginx/config/apps/dev/%{owner}/%{name}.conf',
|
360
|
+
usr: '/var/lib/nginx/config/apps/usr/%{owner}/%{name}.conf',
|
361
|
+
sys: '/var/lib/nginx/config/apps/sys/%{name}.conf'
|
362
|
+
}
|
363
|
+
self.app_root = {
|
364
|
+
dev: '~%{owner}/ondemand/dev/%{name}',
|
365
|
+
usr: '/var/www/ood/apps/usr/%{owner}/gateway/%{name}',
|
366
|
+
sys: '/var/www/ood/apps/sys/%{name}'
|
367
|
+
}
|
368
|
+
self.app_request_uri = {
|
369
|
+
dev: '/dev/%{name}',
|
370
|
+
usr: '/usr/%{owner}/%{name}',
|
371
|
+
sys: '/sys/%{name}'
|
372
|
+
}
|
373
|
+
self.app_request_regex = {
|
374
|
+
dev: '^/dev/(?<name>[-\w.]+)',
|
375
|
+
usr: '^/usr/(?<owner>[\w]+)/(?<name>[-\w.]+)',
|
376
|
+
sys: '^/sys/(?<name>[-\w.]+)'
|
377
|
+
}
|
378
|
+
self.app_token = {
|
379
|
+
dev: 'dev/%{owner}/%{name}',
|
380
|
+
usr: 'usr/%{owner}/%{name}',
|
381
|
+
sys: 'sys/%{name}'
|
382
|
+
}
|
383
|
+
self.app_passenger_env = {
|
384
|
+
dev: 'development',
|
385
|
+
usr: 'production',
|
386
|
+
sys: 'production'
|
387
|
+
}
|
388
|
+
|
389
|
+
self.user_regex = '[\w@\.\-]+'
|
390
|
+
self.min_uid = 1000
|
391
|
+
self.disabled_shell = '/access/denied'
|
392
|
+
|
393
|
+
read_configuration(default_config_path) if File.file?(default_config_path)
|
394
|
+
end
|
395
|
+
|
396
|
+
# Read in a configuration from a file
|
397
|
+
# @param file [String] path to the yaml configuration file
|
398
|
+
# @return [void]
|
399
|
+
def read_configuration(file)
|
400
|
+
config_hash = symbolize(YAML.load_file(file)) || {}
|
401
|
+
config_hash.each do |k,v|
|
402
|
+
if instance_variable_defined? "@#{k}"
|
403
|
+
self.send("#{k}=", v)
|
404
|
+
else
|
405
|
+
$stderr.puts %{Warning: invalid configuration option "#{k}"}
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
private
|
411
|
+
# Recursively symbolize keys in hash
|
412
|
+
def symbolize(obj)
|
413
|
+
return obj.each_with_object({}) {|(k, v), h| h[k.to_sym] = symbolize(v)} if obj.is_a? Hash
|
414
|
+
return obj.each_with_object([]) {|v, a| a << symbolize(v)} if obj.is_a? Array
|
415
|
+
return obj
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module NginxStage
|
2
|
+
# The root exception class that all NginxStage-specific exceptions inherit from
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
# An exception raised when attempting to set an invalid configuration option
|
6
|
+
class InvalidConfigOption < Error; end
|
7
|
+
|
8
|
+
# An exception raised when attempting to resolve an option that doesn't exist
|
9
|
+
class MissingOption < Error; end
|
10
|
+
|
11
|
+
# An exception raised when attempting to resolve a command that doesn't exist
|
12
|
+
class MissingCommand < Error; end
|
13
|
+
|
14
|
+
# An exception raised when attempting to resolve a command that is invalid
|
15
|
+
class InvalidCommand < Error; end
|
16
|
+
|
17
|
+
# An exception raised when attempting to resolve a user that is invalid
|
18
|
+
class InvalidUser < Error; end
|
19
|
+
|
20
|
+
# An exception raised when attempting to resolve a socket that already exists
|
21
|
+
class InvalidSocket < Error; end
|
22
|
+
|
23
|
+
# An exception raised when attempting to resolve an invalid request option
|
24
|
+
class InvalidRequest < Error; end
|
25
|
+
|
26
|
+
# An exception raised when attempting to resolve an invalid sub-uri option
|
27
|
+
class InvalidSubUri < Error; end
|
28
|
+
|
29
|
+
# An exception raised when attempting to resolve an invalid app-init-url option
|
30
|
+
class InvalidAppInitUrl < Error; end
|
31
|
+
|
32
|
+
# An exception raised when attempting to read a pid file that doesn't exist
|
33
|
+
class MissingPidFile < Error; end
|
34
|
+
|
35
|
+
# An exception raised when attempting to read an invalid pid file
|
36
|
+
class InvalidPidFile < Error; end
|
37
|
+
|
38
|
+
# An exception raised when a Pid file has a process id that isn't running
|
39
|
+
class StalePidFile < Error; end
|
40
|
+
|
41
|
+
# An exception raised when attempting to access a socket file that doesn't exist
|
42
|
+
class MissingSocketFile < Error; end
|
43
|
+
|
44
|
+
# An exception raised when attempting to access an invalid socket file
|
45
|
+
class InvalidSocketFile < Error; end
|
46
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'open3'
|
4
|
+
|
5
|
+
require_relative "generator_helpers"
|
6
|
+
|
7
|
+
module NginxStage
|
8
|
+
# Base class for objects that add new sub-commands to NginxStage. {Generator}
|
9
|
+
# is basically a class with helper methods and the ability to invoke all
|
10
|
+
# callback methods in a sequence.
|
11
|
+
class Generator
|
12
|
+
extend GeneratorHelpers
|
13
|
+
|
14
|
+
# Adds a new hook method that is invoked in the order it is defined
|
15
|
+
# @param name [Symbol] unique key defining callback method
|
16
|
+
# @yield The body of the generator's callback
|
17
|
+
# @return [void]
|
18
|
+
def self.add_hook(name, &block)
|
19
|
+
self.hooks[name] = block
|
20
|
+
end
|
21
|
+
|
22
|
+
# Removes a hook method from the callback chain
|
23
|
+
# @param name [Symbol] unique key defining callback method
|
24
|
+
# @return [void]
|
25
|
+
def self.rem_hook(name)
|
26
|
+
self.hooks.delete(name)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns a hash of callback methods in the order they will be invoked
|
30
|
+
# @return [Hash] the callback methods
|
31
|
+
def self.hooks
|
32
|
+
@hooks ||= from_superclass(:hooks, {})
|
33
|
+
end
|
34
|
+
|
35
|
+
# Adds a new option expected from CLI and treats it as an attribute
|
36
|
+
# @param name [Symbol] unique key defining option
|
37
|
+
# @yield The body of the option's callback which should return a hash
|
38
|
+
# @return [void]
|
39
|
+
def self.add_option(name, &block)
|
40
|
+
attr_reader name
|
41
|
+
self._options[name] = block
|
42
|
+
end
|
43
|
+
|
44
|
+
# Removes an option expected from the CLI and removes attribute method
|
45
|
+
# @param name [Symbol] unique key defining option
|
46
|
+
# @return [void]
|
47
|
+
def self.rem_option(name)
|
48
|
+
undef name
|
49
|
+
self._options.delete(name)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a hash of options with callbacks that return a hash of their
|
53
|
+
# attributes
|
54
|
+
# @return [Hash] a hash of options with the corresponding callback
|
55
|
+
def self._options
|
56
|
+
@options ||= from_superclass(:_options, {})
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns a hash of options that point to a hash of their attributes
|
60
|
+
# @return [Hash] a hash of options with the corresponding hash of attributes
|
61
|
+
def self.options
|
62
|
+
Hash[self._options.map { |k,v| [k, v.call] }]
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the description of generator
|
66
|
+
# @return [String] description of generator
|
67
|
+
def self.desc(desc = nil)
|
68
|
+
@desc ||= desc
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the footer description of generator
|
72
|
+
# @return [String] footer description of generator
|
73
|
+
def self.footer(footer = nil)
|
74
|
+
@footer ||= footer
|
75
|
+
end
|
76
|
+
|
77
|
+
# @param opts [Hash] various options for controlling the behavior of the generator
|
78
|
+
def initialize(opts = {})
|
79
|
+
self.class.options.each do |k,v|
|
80
|
+
value = opts.fetch(k) do
|
81
|
+
raise MissingOption, "missing option: #{k}" if v[:required]
|
82
|
+
v[:default]
|
83
|
+
end
|
84
|
+
value = v[:before_init].call(value) if v[:before_init]
|
85
|
+
instance_variable_set("@#{k}", value)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Invokes all the callbacks in the order they are defined in the {hooks}
|
90
|
+
# hash
|
91
|
+
# @return [void]
|
92
|
+
def invoke
|
93
|
+
self.class.hooks.each {|k,v| self.instance_eval(&v)}
|
94
|
+
end
|
95
|
+
|
96
|
+
# Gets an ERB template at the relative source, executes it and makes a copy
|
97
|
+
# at the relative destination
|
98
|
+
# @param source [String] the relative path to the source file
|
99
|
+
# @param destination [String] the relative path to the destination file
|
100
|
+
# @return [void]
|
101
|
+
def template(source, destination)
|
102
|
+
data = File.read File.join(NginxStage.template_root, source)
|
103
|
+
create_file destination, render(data)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Create a new file at the destination path with the given data
|
107
|
+
# @param destination [String] the relative path to the destination file
|
108
|
+
# @param data [String] the given data
|
109
|
+
# @return [void]
|
110
|
+
def create_file(destination, data = "")
|
111
|
+
empty_directory File.dirname(destination)
|
112
|
+
File.open(destination, "wb", 0644) { |f| f.write data }
|
113
|
+
end
|
114
|
+
|
115
|
+
# Create an empty directory if it doesn't already exist
|
116
|
+
# @param destination [String] the directory path
|
117
|
+
# @param mode [Fixnum] the mode to set the directory as
|
118
|
+
# @return [void]
|
119
|
+
def empty_directory(destination, mode: 0755)
|
120
|
+
FileUtils.mkdir_p destination, mode: mode
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
# Retrieves a value from superclass. If it reaches the baseclass,
|
125
|
+
# returns default
|
126
|
+
def self.from_superclass(method, default = nil)
|
127
|
+
if self == NginxStage::Generator || !superclass.respond_to?(method, true)
|
128
|
+
default
|
129
|
+
else
|
130
|
+
superclass.send(method).dup
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Use ERB to render templates
|
135
|
+
def render(data)
|
136
|
+
ERB.new(data, nil, '-').result(binding)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module NginxStage
|
2
|
+
# Module that adds common options to generators.
|
3
|
+
module GeneratorHelpers
|
4
|
+
# Add support for accepting USER as an option
|
5
|
+
# @param required [Boolean] whether user option is required
|
6
|
+
# @return [void]
|
7
|
+
def add_user_support(required: true)
|
8
|
+
# @!method user
|
9
|
+
# The user that the per-user NGINX will run as
|
10
|
+
# @return [User] the user of the nginx process
|
11
|
+
# @raise [MissingOption] if user isn't supplied
|
12
|
+
self.add_option :user do
|
13
|
+
{
|
14
|
+
opt_args: ["-u", "--user=USER", "# The USER of the per-user nginx process"],
|
15
|
+
required: required,
|
16
|
+
before_init: -> (user) do
|
17
|
+
raise InvalidUser, "invalid user name syntax: #{user}" unless user =~ NginxStage.user_regex
|
18
|
+
user ? User.new(user) : nil
|
19
|
+
end
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
if required
|
24
|
+
# Validate that the user isn't a special user (i.e., `root`)
|
25
|
+
self.add_hook :validate_user_not_special do
|
26
|
+
min_uid = NginxStage.min_uid
|
27
|
+
if user.uid < min_uid
|
28
|
+
raise InvalidUser, "user is special: #{user} (#{user.uid} < #{min_uid})"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Add support for accepting SKIP_NGINX as an option
|
35
|
+
# @return [void]
|
36
|
+
def add_skip_nginx_support
|
37
|
+
# @!method skip_nginx
|
38
|
+
# Whether we skip calling the NGINX process
|
39
|
+
# @return [Boolean] if true, skip calling the nginx binary
|
40
|
+
add_option :skip_nginx do
|
41
|
+
{
|
42
|
+
opt_args: ["-N", "--[no-]skip-nginx", "# Skip execution of the per-user nginx process", "# Default: false"],
|
43
|
+
default: false
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Add support for accepting SUB_URI as an option
|
49
|
+
# @return [void]
|
50
|
+
def add_sub_uri_support
|
51
|
+
# @!method sub_uri
|
52
|
+
# The sub-uri that distinguishes the per-user NGINX process
|
53
|
+
# @example An app is requested through '/pun/usr/user/appname/...'
|
54
|
+
# sub_uri #=> "/pun"
|
55
|
+
# @return [String] the sub-uri for nginx
|
56
|
+
add_option :sub_uri do
|
57
|
+
{
|
58
|
+
opt_args: ["-i", "--sub-uri=SUB_URI", "# The SUB_URI that requests the per-user nginx", "# Default: ''"],
|
59
|
+
default: '',
|
60
|
+
before_init: -> (sub_uri) do
|
61
|
+
raise InvalidSubUri, "invalid sub-uri syntax: #{sub_uri}" if sub_uri =~ /[^-\w\/]/
|
62
|
+
sub_uri
|
63
|
+
end
|
64
|
+
}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|