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,38 @@
|
|
1
|
+
module NginxStage
|
2
|
+
# This generator cleans up any staged app configs whose app root doesn't
|
3
|
+
# exist anymore. It will also print out the paths for the app configs it
|
4
|
+
# deleted.
|
5
|
+
class AppCleanGenerator < Generator
|
6
|
+
desc 'Clean up any staged app configs that point to deleted apps'
|
7
|
+
|
8
|
+
footer <<-EOF.gsub(/^ {4}/, '')
|
9
|
+
Examples:
|
10
|
+
To clean up all stale app configs:
|
11
|
+
|
12
|
+
nginx_stage app_clean
|
13
|
+
|
14
|
+
this displays the paths of the app configs it deleted.
|
15
|
+
EOF
|
16
|
+
|
17
|
+
# Delete staged app configs whose app doesn't exist anymore. Also display
|
18
|
+
# the app configs that are deleted.
|
19
|
+
add_hook :delete_stale_app_configs do
|
20
|
+
NginxStage.staged_apps.each do |env, apps|
|
21
|
+
apps.each do |h|
|
22
|
+
owner = h[:owner]
|
23
|
+
name = h[:name]
|
24
|
+
app_config = NginxStage.app_config_path(env: env, owner: owner, name: name)
|
25
|
+
app_root = NginxStage.app_root(env: env, owner: owner, name: name)
|
26
|
+
unless NginxStage.as_user(owner) { File.directory?(app_root) }
|
27
|
+
begin
|
28
|
+
File.delete app_config
|
29
|
+
puts app_config
|
30
|
+
rescue
|
31
|
+
$stderr.puts "#{$!.to_s}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module NginxStage
|
2
|
+
# This generator stages and generates the NGINX app config. It is also
|
3
|
+
# responsible for reloading the per-user NGINX process after updating the app
|
4
|
+
# config.
|
5
|
+
class AppConfigGenerator < Generator
|
6
|
+
desc 'Generate a new nginx app config and reload process'
|
7
|
+
|
8
|
+
footer <<-EOF.gsub(/^ {4}/, '')
|
9
|
+
Examples:
|
10
|
+
To generate an app config from a URI request and reload the nginx
|
11
|
+
process:
|
12
|
+
|
13
|
+
nginx_stage app --user=bob --sub-uri=/pun --sub-request=/usr/jimmy/fillsim/container/13
|
14
|
+
|
15
|
+
To generate ONLY the app config from a URI request:
|
16
|
+
|
17
|
+
nginx_stage app --user=bob --sub-uri=/pun --sub-request=/usr/jimmy/fillsim --skip-nginx
|
18
|
+
|
19
|
+
this will return the app config path and won't run nginx.
|
20
|
+
EOF
|
21
|
+
|
22
|
+
include AppConfigView
|
23
|
+
|
24
|
+
# The environment the app is run under (parsed from sub_request)
|
25
|
+
# @return [Symbol] environment app is run under
|
26
|
+
attr_accessor :env
|
27
|
+
|
28
|
+
# The owner of the app (parsed from sub_request or assume it is user)
|
29
|
+
# @return [String] owner of app
|
30
|
+
attr_accessor :owner
|
31
|
+
|
32
|
+
# The name of the app
|
33
|
+
# @return [String] name of app
|
34
|
+
attr_accessor :name
|
35
|
+
|
36
|
+
# Accepts `user` as an option and validates user
|
37
|
+
add_user_support
|
38
|
+
|
39
|
+
# Accepts `skip_nginx` as an option
|
40
|
+
add_skip_nginx_support
|
41
|
+
|
42
|
+
# Accepts `sub_uri` as an option
|
43
|
+
add_sub_uri_support
|
44
|
+
|
45
|
+
# @!method sub_request
|
46
|
+
# The remainder of the request after the sub-uri used to determine the
|
47
|
+
# environment and app
|
48
|
+
# @example An app is requested through '/pun/usr/user/appname/...'
|
49
|
+
# sub_request #=> "/usr/user/appname/..."
|
50
|
+
# @return [String] the remainder of the request after sub-uri
|
51
|
+
# @raise [MissingOption] if sub_request isn't supplied
|
52
|
+
add_option :sub_request do
|
53
|
+
{
|
54
|
+
opt_args: ["-r", "--sub-request=SUB_REQUEST", "# The SUB_REQUEST that requests the specified app"],
|
55
|
+
required: true
|
56
|
+
# sub-request is validated in `NginxStage::parse_app_request`
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
# Parse the sub_request for the environment, owner, and app name
|
61
|
+
add_hook :parse_sub_request do
|
62
|
+
info = NginxStage.parse_app_request(request: sub_request)
|
63
|
+
self.env = info.fetch(:env)
|
64
|
+
self.owner = info.fetch(:owner, user)
|
65
|
+
self.name = info.fetch(:name, nil)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Validate that the path to the app exists on the local filesystem
|
69
|
+
add_hook :validate_app_root do
|
70
|
+
raise InvalidRequest, "invalid app root: #{app_root}" unless NginxStage.as_user(user) { File.directory?(app_root) }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Generate the NGINX app config from the 'app.conf.erb' template
|
74
|
+
add_hook :create_config do
|
75
|
+
template "app.conf.erb", app_config_path
|
76
|
+
end
|
77
|
+
|
78
|
+
# Restart the per-user NGINX process (exit quietly on success)
|
79
|
+
add_hook :exec_nginx do
|
80
|
+
if !skip_nginx
|
81
|
+
if File.file? NginxStage.pun_pid_path(user: user)
|
82
|
+
o, s = Open3.capture2e([NginxStage.nginx_bin, "(#{user})"], *NginxStage.nginx_args(user: user, signal: :stop))
|
83
|
+
abort(o) unless s.success?
|
84
|
+
end
|
85
|
+
o, s = Open3.capture2e([NginxStage.nginx_bin, "(#{user})"], *NginxStage.nginx_args(user: user))
|
86
|
+
s.success? ? exit : abort(o)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# If we skip nginx, then output the path to the generated NGINX app config
|
91
|
+
add_hook :output_app_config_path do
|
92
|
+
puts app_config_path
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
# NGINX app config path
|
97
|
+
def app_config_path
|
98
|
+
NginxStage.app_config_path(env: env, owner: owner, name: name)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module NginxStage
|
2
|
+
# This generator lists all staged apps with app configs.
|
3
|
+
class AppListGenerator < Generator
|
4
|
+
desc 'List all staged app configs'
|
5
|
+
|
6
|
+
footer <<-EOF.gsub(/^ {4}/, '')
|
7
|
+
Examples:
|
8
|
+
To list all staged app configs:
|
9
|
+
|
10
|
+
nginx_stage app_list
|
11
|
+
|
12
|
+
this will return the paths to all the staged app configs.
|
13
|
+
EOF
|
14
|
+
|
15
|
+
# Displays a list of all staged app configs
|
16
|
+
add_hook :print_staged_app_configs do
|
17
|
+
NginxStage.staged_apps.each do |env, apps|
|
18
|
+
apps.each do |h|
|
19
|
+
puts NginxStage.app_config_path(env: env, owner: h[:owner], name: h[:name])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module NginxStage
|
2
|
+
# This generator resets all app configs with the most current app config
|
3
|
+
# template.
|
4
|
+
class AppResetGenerator < Generator
|
5
|
+
desc 'Reset all staged app configs with the current template'
|
6
|
+
|
7
|
+
footer <<-EOF.gsub(/^ {4}/, '')
|
8
|
+
Examples:
|
9
|
+
To reset all staged app configs using the currently available app
|
10
|
+
config template:
|
11
|
+
|
12
|
+
nginx_stage app_reset --sub-uri=/pun
|
13
|
+
|
14
|
+
this will return the paths to the newly updated app configs.
|
15
|
+
EOF
|
16
|
+
|
17
|
+
include AppConfigView
|
18
|
+
|
19
|
+
# The environment the app is run under (parsed from sub_request)
|
20
|
+
# @return [Symbol] environment app is run under
|
21
|
+
attr_accessor :env
|
22
|
+
|
23
|
+
# The owner of the app (parsed from sub_request or assume it is user)
|
24
|
+
# @return [String] owner of app
|
25
|
+
attr_accessor :owner
|
26
|
+
|
27
|
+
# The name of the app
|
28
|
+
# @return [String] name of app
|
29
|
+
attr_accessor :name
|
30
|
+
|
31
|
+
# Accepts `sub_uri` as an option
|
32
|
+
add_sub_uri_support
|
33
|
+
|
34
|
+
# Updates all staged app configs with current template and displays paths
|
35
|
+
# to user
|
36
|
+
add_hook :update_app_configs do
|
37
|
+
NginxStage.staged_apps.each do |env, apps|
|
38
|
+
apps.each do |h|
|
39
|
+
self.env = env
|
40
|
+
self.owner = h[:owner]
|
41
|
+
self.name = h[:name]
|
42
|
+
template "app.conf.erb", app_config_path
|
43
|
+
puts app_config_path
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
# NGINX app config path
|
50
|
+
def app_config_path
|
51
|
+
NginxStage.app_config_path(env: env, owner: owner, name: name)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module NginxStage
|
2
|
+
# This generator cleans all running per-user NGINX processes that are
|
3
|
+
# inactive (i.e., not active connections).
|
4
|
+
class NginxCleanGenerator < Generator
|
5
|
+
desc 'Clean all user running PUNs with no active connections'
|
6
|
+
|
7
|
+
footer <<-EOF.gsub(/^ {4}/, '')
|
8
|
+
Examples:
|
9
|
+
To clean up any running per-user nginx process with no active
|
10
|
+
connections:
|
11
|
+
|
12
|
+
nginx_stage nginx_clean
|
13
|
+
|
14
|
+
this displays the users who had their PUNs shutdown.
|
15
|
+
|
16
|
+
To clean up ALL running per-user nginx processes whether it has an
|
17
|
+
active connection or not:
|
18
|
+
|
19
|
+
nginx_stage nginx_clean --force
|
20
|
+
|
21
|
+
this also displays the users who had their PUNs shutdown.
|
22
|
+
|
23
|
+
To ONLY display the users with inactive PUNs:
|
24
|
+
|
25
|
+
nginx_stage nginx_clean --skip-nginx
|
26
|
+
|
27
|
+
this won't terminate their per-user nginx process.
|
28
|
+
EOF
|
29
|
+
|
30
|
+
# Whether we forcefully kill all PUNs even if they have connections
|
31
|
+
add_option :force do
|
32
|
+
{
|
33
|
+
opt_args: ["-f", "--[no-]force", "# Force clean ALL per-user nginx processes", "# Default: false"],
|
34
|
+
default: false
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Accepts `skip_nginx` as an option
|
39
|
+
add_skip_nginx_support
|
40
|
+
|
41
|
+
# Find users with PUNs that have no active sessions and kill the process
|
42
|
+
add_hook :delete_puns_of_users_with_no_sessions do
|
43
|
+
NginxStage.active_users.each do |u|
|
44
|
+
begin
|
45
|
+
pid_path = PidFile.new NginxStage.pun_pid_path(user: u)
|
46
|
+
raise StalePidFile, "stale pid file: #{pid_path}" unless pid_path.running_process?
|
47
|
+
socket = SocketFile.new NginxStage.pun_socket_path(user: u)
|
48
|
+
if socket.sessions.zero? || force
|
49
|
+
puts u
|
50
|
+
if !skip_nginx
|
51
|
+
o, s = Open3.capture2e(NginxStage.nginx_bin, *NginxStage.nginx_args(user: u, signal: :stop))
|
52
|
+
$stderr.puts o unless s.success?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
rescue
|
56
|
+
$stderr.puts "#{$!.to_s}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module NginxStage
|
2
|
+
# This generator lists all running per-user NGINX processes.
|
3
|
+
class NginxListGenerator < Generator
|
4
|
+
desc 'List all user running PUNs'
|
5
|
+
|
6
|
+
footer <<-EOF.gsub(/^ {4}/, '')
|
7
|
+
Examples:
|
8
|
+
To list all active per-user nginx processes:
|
9
|
+
|
10
|
+
nginx_stage nginx_list
|
11
|
+
|
12
|
+
this lists all users who have actively running PUNs.
|
13
|
+
EOF
|
14
|
+
|
15
|
+
# Display active users
|
16
|
+
add_hook :display_active_users do
|
17
|
+
NginxStage.active_users.each do |u|
|
18
|
+
puts u
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module NginxStage
|
2
|
+
# This generator generates/controls the per-user NGINX process.
|
3
|
+
class NginxProcessGenerator < Generator
|
4
|
+
desc 'Generate/control a per-user nginx process'
|
5
|
+
|
6
|
+
footer <<-EOF.gsub(/^ {4}/, '')
|
7
|
+
Examples:
|
8
|
+
To stop Bob's nginx process:
|
9
|
+
|
10
|
+
nginx_stage nginx --user=bob --signal=stop
|
11
|
+
|
12
|
+
which sends a `stop` signal to Bob's per-user NGINX process.
|
13
|
+
|
14
|
+
If `--skip-nginx` is supplied it returns the system-level command
|
15
|
+
that would have been called.
|
16
|
+
EOF
|
17
|
+
|
18
|
+
# Accepts `user` as an option and validates user
|
19
|
+
add_user_support
|
20
|
+
|
21
|
+
# Accepts `skip_nginx` as an option
|
22
|
+
add_skip_nginx_support
|
23
|
+
|
24
|
+
# @!method signal
|
25
|
+
# The signal to send to the per-user NGINX process
|
26
|
+
# @return [String] nginx signal
|
27
|
+
add_option :signal do
|
28
|
+
{
|
29
|
+
opt_args: ["-s", "--signal=SIGNAL", NginxStage.nginx_signals, "# Send SIGNAL to per-user nginx process: #{NginxStage.nginx_signals.join('/')}"],
|
30
|
+
default: nil
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
# Run the per-user NGINX process (exit quietly on success)
|
35
|
+
add_hook :exec_nginx do
|
36
|
+
if !skip_nginx
|
37
|
+
o, s = Open3.capture2e([NginxStage.nginx_bin, "(#{user})"], *NginxStage.nginx_args(user: user, signal: signal))
|
38
|
+
s.success? ? exit : abort(o)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# If skip nginx, then output nginx command
|
43
|
+
add_hook :output_nginx_cmd do
|
44
|
+
puts "#{NginxStage.nginx_bin} #{NginxStage.nginx_args(user: user, signal: signal).join(' ')}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module NginxStage
|
2
|
+
# This generator shows the state of the running per-user NGINX process.
|
3
|
+
class NginxShowGenerator < Generator
|
4
|
+
desc 'Show the details for a given per-user nginx process'
|
5
|
+
|
6
|
+
footer <<-EOF.gsub(/^ {4}/, '')
|
7
|
+
Examples:
|
8
|
+
To display the details of a running per-user nginx process:
|
9
|
+
|
10
|
+
nginx_stage nginx_show --user=bob
|
11
|
+
|
12
|
+
this also displays the number of active sessions connected to this PUN.
|
13
|
+
EOF
|
14
|
+
|
15
|
+
# Accepts 'user' as an option and validates user
|
16
|
+
add_user_support
|
17
|
+
|
18
|
+
# Display the chosen user
|
19
|
+
add_hook :display_user do
|
20
|
+
puts "User: #{user}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Check that pid is valid & clean up any stale files
|
24
|
+
add_hook :check_pid_is_process do
|
25
|
+
pid_file = PidFile.new pid_path
|
26
|
+
raise StalePidFile, "stale pid file: #{pid_path}" unless pid_file.running_process?
|
27
|
+
puts "Instance: #{pid_file.pid}"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Check for active sessions on Unix domain socket
|
31
|
+
add_hook :check_socket_for_active_sessions do
|
32
|
+
socket = SocketFile.new socket_path
|
33
|
+
puts "Socket: #{socket}"
|
34
|
+
puts "Sessions: #{socket.sessions}"
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
# Path to the user's per-user NGINX pid file
|
39
|
+
def pid_path
|
40
|
+
NginxStage.pun_pid_path(user: user)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Path to the user's per-user NGINX socket file
|
44
|
+
def socket_path
|
45
|
+
NginxStage.pun_socket_path(user: user)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module NginxStage
|
2
|
+
# This generator stages and generates the per-user NGINX environment.
|
3
|
+
class PunConfigGenerator < Generator
|
4
|
+
desc 'Generate a new per-user nginx config and process'
|
5
|
+
|
6
|
+
footer <<-EOF.gsub(/^ {4}/, '')
|
7
|
+
Examples:
|
8
|
+
To generate a per-user nginx environment & launch nginx:
|
9
|
+
|
10
|
+
nginx_stage pun --user=bob --app-init-url='http://www.ood.com/nginx/init?redir=$http_x_forwarded_escaped_uri'
|
11
|
+
|
12
|
+
this will add a URI redirect if the user accesses an app that doesn't exist.
|
13
|
+
|
14
|
+
To generate ONLY the per-user nginx environment:
|
15
|
+
|
16
|
+
nginx_stage pun --user=bob --skip-nginx
|
17
|
+
|
18
|
+
this will return the per-user nginx config path and won't run nginx. In addition
|
19
|
+
it will remove the URI redirect from the config unless we specify `--app-init-url`.
|
20
|
+
EOF
|
21
|
+
|
22
|
+
include PunConfigView
|
23
|
+
|
24
|
+
# Accepts `user` as an option and validates user
|
25
|
+
add_user_support
|
26
|
+
|
27
|
+
# Block starting up PUNs for users with disabled shells
|
28
|
+
add_hook :block_user_with_disabled_shell do
|
29
|
+
raise InvalidUser, "user has disabled shell: #{user}" if user.shell == NginxStage.disabled_shell
|
30
|
+
end
|
31
|
+
|
32
|
+
# Accepts `skip_nginx` as an option
|
33
|
+
add_skip_nginx_support
|
34
|
+
|
35
|
+
# @!method app_init_url
|
36
|
+
# The app initialization URL the user is redirected to if can't find the
|
37
|
+
# app in the per-user NGINX config
|
38
|
+
# @return [String] app init redirect url
|
39
|
+
add_option :app_init_url do
|
40
|
+
{
|
41
|
+
opt_args: ["-a", "--app-init-url=APP_INIT_URL", "# The user is redirected to the APP_INIT_URL if app doesn't exist"],
|
42
|
+
default: nil,
|
43
|
+
before_init: -> (uri) do
|
44
|
+
raise InvalidAppInitUri, "invalid app-init-url syntax: #{uri}" if uri =~ /[^-\w\/?$=&.:]/
|
45
|
+
uri
|
46
|
+
end
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
# Create the user's personal per-user NGINX `/tmp` location for the various
|
51
|
+
# nginx cache directories
|
52
|
+
add_hook :create_user_tmp_root do
|
53
|
+
empty_directory tmp_root
|
54
|
+
end
|
55
|
+
|
56
|
+
# Create the user's personal per-user NGINX `/log` location for the various
|
57
|
+
# nginx log files (e.g., 'error.log' & 'access.log')
|
58
|
+
add_hook :create_user_log_roots do
|
59
|
+
empty_directory File.dirname(error_log_path)
|
60
|
+
empty_directory File.dirname(access_log_path)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Create per-user NGINX pid root
|
64
|
+
add_hook :create_pid_root do
|
65
|
+
empty_directory File.dirname(pid_path)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Create and secure the nginx socket root. The socket file needs to be only
|
69
|
+
# accessible by the reverse proxy user.
|
70
|
+
add_hook :create_and_secure_socket_root do
|
71
|
+
socket_root = File.dirname(socket_path)
|
72
|
+
empty_directory socket_root
|
73
|
+
FileUtils.chmod 0700, socket_root
|
74
|
+
FileUtils.chown NginxStage.proxy_user, nil, socket_root if Process.uid == 0
|
75
|
+
end
|
76
|
+
|
77
|
+
# Generate the per-user NGINX config from the 'pun.conf.erb' template
|
78
|
+
add_hook :create_config do
|
79
|
+
template "pun.conf.erb", config_path
|
80
|
+
end
|
81
|
+
|
82
|
+
# Run the per-user NGINX process (exit quietly on success)
|
83
|
+
add_hook :exec_nginx do
|
84
|
+
if !skip_nginx
|
85
|
+
o, s = Open3.capture2e([NginxStage.nginx_bin, "(#{user})"], *NginxStage.nginx_args(user: user))
|
86
|
+
s.success? ? exit : abort(o)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# If skip nginx, then output path to the generated per-user NGINX config
|
91
|
+
add_hook :output_pun_config_path do
|
92
|
+
puts config_path
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
private
|
97
|
+
# per-user NGINX config path
|
98
|
+
def config_path
|
99
|
+
NginxStage.pun_config_path(user: user)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module NginxStage
|
2
|
+
# A class to handle a PID file
|
3
|
+
class PidFile
|
4
|
+
# Path of the PID file
|
5
|
+
# @return [String] the path of the pid file
|
6
|
+
attr_reader :pid_path
|
7
|
+
|
8
|
+
# Process id that describes this object
|
9
|
+
# @return [Fixnum] the pid object was initialized with
|
10
|
+
attr_reader :pid
|
11
|
+
|
12
|
+
# @param pid [Fixnum] the pid describing a process
|
13
|
+
# @raise [MissingPidFile] if pid file doesn't exist in file system
|
14
|
+
# @raise [InvalidPidFile] if it is an invalid pid file
|
15
|
+
def initialize(pid_path)
|
16
|
+
@pid_path = pid_path
|
17
|
+
raise MissingPidFile, "missing PID file: #{pid_path}" unless File.exist?(pid_path)
|
18
|
+
raise InvalidPidFile, "invalid PID file: #{pid_path}" unless File.file?(pid_path)
|
19
|
+
@pid = File.read(pid_path).to_i
|
20
|
+
end
|
21
|
+
|
22
|
+
# Whether the corresponding pid is a running process
|
23
|
+
# @return [Boolean] whether it is a running process
|
24
|
+
def running_process?
|
25
|
+
Process.getpgid @pid
|
26
|
+
true
|
27
|
+
rescue Errno::ESRCH
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
# Convert object to string
|
32
|
+
# @return [String] the pid path
|
33
|
+
def to_s
|
34
|
+
pid_path.to_s
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module NginxStage
|
2
|
+
# A class describing a Unix domain socket
|
3
|
+
class SocketFile
|
4
|
+
# Path to Unix domain socket file
|
5
|
+
# @return [String] path to unix domain socket file
|
6
|
+
attr_reader :socket
|
7
|
+
|
8
|
+
# @param socket [String] path to unix domain socket file
|
9
|
+
# @raise [MissingSocketFile] if socket file doesn't exist in file system
|
10
|
+
# @raise [InvalidSocketFile] if supplied file isn't a valid socket file
|
11
|
+
def initialize(socket)
|
12
|
+
@socket = socket
|
13
|
+
raise MissingSocketFile, "missing socket file: #{socket}" unless File.exist?(socket)
|
14
|
+
raise InvalidSocketFile, "invalid socket file: #{socket}" unless File.socket?(socket)
|
15
|
+
@processes = get_processes
|
16
|
+
end
|
17
|
+
|
18
|
+
# The number of active sessions connected to this socket
|
19
|
+
# @return [Fixnum] number of active connections
|
20
|
+
def sessions
|
21
|
+
# generate array of inodes
|
22
|
+
ary_inodes = @processes.map{|h| h[:inode]}.reduce([], :+)
|
23
|
+
|
24
|
+
# count number of inodes without partner (assuming these are connected to
|
25
|
+
# apache proxy instead of root nginx process)
|
26
|
+
ary_inodes.group_by{|e| e}.select{|k,v| v.size == 1}.map(&:first).count
|
27
|
+
end
|
28
|
+
|
29
|
+
# Convert object to string
|
30
|
+
# @return [String] path to socket file
|
31
|
+
def to_s
|
32
|
+
socket
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def get_processes
|
37
|
+
str = `lsof -F piu #{socket}`
|
38
|
+
ary = []
|
39
|
+
str.split(/\n/).each do |l|
|
40
|
+
if /^p(?<pid>\d+)$/ =~ l
|
41
|
+
ary << {pid: pid, uid: nil, inode: []}
|
42
|
+
elsif /^u(?<uid>\d+)$/ =~ l
|
43
|
+
ary.last[:uid] = uid
|
44
|
+
elsif /^i(?<inode>\d+)$/ =~ l
|
45
|
+
ary.last[:inode] << inode
|
46
|
+
end
|
47
|
+
end
|
48
|
+
ary
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module NginxStage
|
4
|
+
# A String-like Class that includes helper methods to better describe the
|
5
|
+
# user on the local system.
|
6
|
+
class User
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
# @!method name
|
10
|
+
# The user name
|
11
|
+
# @return [String] the user name
|
12
|
+
# @!method uid
|
13
|
+
# The user's id
|
14
|
+
# @return [Integer] the user id
|
15
|
+
# @!method gid
|
16
|
+
# The user's group id
|
17
|
+
# @return [Integer] the group id
|
18
|
+
# @!method gecos
|
19
|
+
# The user's real name
|
20
|
+
# @return [String] the real name
|
21
|
+
# @!method dir
|
22
|
+
# The user's home directory
|
23
|
+
# @return [String] the home path
|
24
|
+
# @!method shell
|
25
|
+
# The user's shell
|
26
|
+
# @return [String] the shell
|
27
|
+
delegate [:name, :uid, :gid, :gecos, :dir, :shell] => :@passwd
|
28
|
+
|
29
|
+
# List of all groups that user belongs to
|
30
|
+
# @return [Array<String>] list of groups user is in
|
31
|
+
attr_reader :groups
|
32
|
+
|
33
|
+
# @param user [String] the user name defining this object
|
34
|
+
# @raise [InvalidUser] if user doesn't exist on local system
|
35
|
+
def initialize(user)
|
36
|
+
@passwd = Etc.getpwnam user.to_s
|
37
|
+
@group = Etc.getgrgid gid
|
38
|
+
@groups = get_groups
|
39
|
+
rescue ArgumentError
|
40
|
+
raise InvalidUser, "user doesn't exist: #{user}"
|
41
|
+
end
|
42
|
+
|
43
|
+
# User's primary group name
|
44
|
+
# @return [String] the primary group name
|
45
|
+
def group
|
46
|
+
@group.name
|
47
|
+
end
|
48
|
+
|
49
|
+
# Members of user's primary group
|
50
|
+
# @return [Array<String>] list of users in primary group
|
51
|
+
def group_mem
|
52
|
+
@group.mem
|
53
|
+
end
|
54
|
+
|
55
|
+
# Convert object to string using user name as string value
|
56
|
+
# @return [String] the user name
|
57
|
+
def to_s
|
58
|
+
@passwd.name
|
59
|
+
end
|
60
|
+
|
61
|
+
# This object is string-like and returns the user name when treated as a
|
62
|
+
# string
|
63
|
+
# @return [String] the user name
|
64
|
+
def to_str
|
65
|
+
@passwd.name
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
# Use `id` to get list of groups as the /etc/group file can give
|
70
|
+
# erroneous results
|
71
|
+
def get_groups
|
72
|
+
`id -nG #{name}`.split(' ')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|