Class: Potluck::Nginx

Inherits:
Service
  • Object
show all
Defined in:
lib/potluck/nginx.rb,
lib/potluck/nginx/ssl.rb,
lib/potluck/nginx/util.rb,
lib/potluck/nginx/version.rb

Overview

A Ruby interface for configuring and controlling Nginx. Each instance of this class manages a separate Nginx configuration file, which is loaded and unloaded from the base Nginx configuration when #start and #stop are called, respectively. Any number of Ruby processes can thus each manage their own Nginx configuration and control whether or not it is active without interfering with any other instances or non-Ruby processes leveraging Nginx.

Defined Under Namespace

Classes: SSL, Util

Constant Summary collapse

CONFIG_NAME_ACTIVE =
'nginx.conf'
CONFIG_NAME_INACTIVE =
'nginx-stopped.conf'
ACTIVE_CONFIG_PATTERN =
File.join(DIR, '*', CONFIG_NAME_ACTIVE).freeze
TEST_CONFIG_REGEX =
/nginx: configuration file (?<config>.+) test (failed|is successful)/.freeze
INCLUDE_REGEX =
/^ *include +#{Regexp.escape(ACTIVE_CONFIG_PATTERN)} *;/.freeze
NON_LAUNCHCTL_COMMANDS =
{
  status: 'ps aux | grep \'[n]ginx: master process\'',
  start: 'nginx',
  stop: 'nginx -s stop',
}.freeze
VERSION =
'0.0.7'

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hosts, port, subdomains: nil, ssl: nil, one_host: false, www: nil, multiple_slashes: nil, multiple_question_marks: nil, trailing_slash: nil, trailing_question_mark: nil, config: {}, ensure_host_entries: false, **args) ⇒ Nginx

Creates a new instance.

  • hosts - One or more hosts.

  • port - Port that the upstream (Ruby web server) is running on.

  • subdomains - One or more subdomains (optional).

  • ssl - SSL configuration arguments to pass to SSL.new (optional).

  • one_host - True if URLs should be normalized to the first host in hosts (optional, default: false).

  • www - true if URLs should be normalized to include ‘www.’ prefix, false to exclude ‘www.’, and nil if either is acceptable (optional, default: nil).

  • multiple_slashes - false if any occurrence of multiple slashes in the path portion of the URL should be normalized to a single slash (optional, default: nil).

  • multiple_question_marks - false if multiple question marks in the URL signifying the start of the query string should be normalized to a single question mark (optional, default: nil).

  • trailing_slash - true if URLs should be normalized to include a trailing slash at the end of the path portion, false to strip any trailing slash, and nil if either is acceptable (optional, default: nil).

  • trailing_question_mark - true if URLs should be normalized to include a trailing question mark when the query string is empty, false to strip any trailing question mark, and nil if either is acceptable (optional, default: nil).

  • config - Nginx configuration hash; see #config (optional).

  • ensure_host_entries - True if hosts should be added to system /etc/hosts file as mappings to localhost (optional, default: false).

  • args - Arguments to pass to Potluck::Service.new (optional).



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
# File 'lib/potluck/nginx.rb', line 57

def initialize(hosts, port, subdomains: nil, ssl: nil, one_host: false, www: nil, multiple_slashes: nil,
    multiple_question_marks: nil, trailing_slash: nil, trailing_question_mark: nil, config: {},
    ensure_host_entries: false, **args)
  if args[:manage] && !args[:manage].kind_of?(Hash) && !self.class.launchctl?
    args[:manage] = NON_LAUNCHCTL_COMMANDS
  end

  super(**args)

  @hosts = Array(hosts).map { |h| h.sub(/^www\./, '') }.uniq
  @hosts += @hosts.map { |h| "www.#{h}" }
  @host = @hosts.first
  @port = port

  @ensure_host_entries = ensure_host_entries
  @dir = File.join(DIR, @host)
  @ssl = SSL.new(self, @dir, @host, **ssl) if ssl

  @scheme = @ssl ? 'https' : 'http'
  @other_scheme = @ssl ? 'http' : 'https'
  @one_host = !!one_host
  @subdomains = Array(subdomains)
  @www = www
  @multiple_slashes = multiple_slashes
  @multiple_question_marks = multiple_question_marks
  @trailing_slash = trailing_slash
  @trailing_question_mark = trailing_question_mark
  @additional_config = config

  FileUtils.mkdir_p(@dir)

  @config_file_active = File.join(@dir, CONFIG_NAME_ACTIVE).freeze
  @config_file_inactive = File.join(@dir, CONFIG_NAME_INACTIVE).freeze
end

Class Method Details

.plistObject

Content of the launchctl plist file.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/potluck/nginx.rb', line 145

def self.plist
  super(
    <<~EOS
      <key>ProgramArguments</key>
      <array>
        <string>#{HOMEBREW_PREFIX}/opt/nginx/bin/nginx</string>
        <string>-g</string>
        <string>daemon off;</string>
      </array>
      <key>StandardOutPath</key>
      <string>#{HOMEBREW_PREFIX}/var/log/nginx/access.log</string>
      <key>StandardErrorPath</key>
      <string>#{HOMEBREW_PREFIX}/var/log/nginx/error.log</string>
    EOS
  )
end

.to_nginx_config(hash, indent: 0, repeat: nil) ⇒ Object

Converts a hash to an Nginx configuration file content string. Keys should be strings and values either strings or hashes. A nil value in a hash will result in that key-value pair being omitted.

  • hash - Hash to convert to the string content of an Nginx configuration file.

  • indent - Number of spaces to indent; used when the method is called recursively and should not be set explicitly (optional, default: 0).

  • repeat - Value to prepend to each entry of the hash; used when the method is called recursively and should not be set explicitly (optional).

Symbol keys in hashes are used as special directives. Including repeat: true will cause the parent hash’s key for the child hash to be prefixed to each line of the output. Example:

{
  # ...

  'add_header' => {
    repeat: true,
    'X-Frame-Options' => 'DENY',
    'X-Content-Type-Options' => 'nosniff',
  }
}

Result:

# ...

add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

A hash containing raw: '...' can be used to include a raw chunk of text rather than key-value pairs. Example:

{
  # ...

  'location /' => {
    raw: """
      if ($scheme = https) { ... }
      if ($host ~ ^www.) { ... }
    """,
  }
}

Result:

location / {
  if ($scheme = https) { ... }
  if ($host ~ ^www.) { ... }
}


213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/potluck/nginx.rb', line 213

def self.to_nginx_config(hash, indent: 0, repeat: nil)
  hash.each_with_object(+'') do |(k, v), config|
    next if v.nil?
    next if k == :repeat

    config << (
      if v.kind_of?(Hash)
        if v[:repeat]
          to_nginx_config(v, indent: indent, repeat: k)
        else
          "#{' ' * indent}#{k} {\n#{to_nginx_config(v, indent: indent + 2)}#{' ' * indent}}\n"
        end
      elsif k == :raw
        "#{v.gsub(/^(?=.)/, ' ' * indent)}\n\n"
      else
        "#{' ' * indent}#{"#{repeat} " if repeat}#{k}#{" #{v}" unless v == true};\n"
      end
    )
  end
end

Instance Method Details

#config_file_contentObject

Returns the content for the Nginx configuration file as a string.



138
139
140
# File 'lib/potluck/nginx.rb', line 138

def config_file_content
  self.class.to_nginx_config(config)
end

#reloadObject

Reloads Nginx if it’s managed.



129
130
131
132
133
# File 'lib/potluck/nginx.rb', line 129

def reload
  return unless manage?

  run('nginx -s reload')
end

#startObject

Ensures this instance’s configuration file is active and starts Nginx if it’s managed. If Nginx is already running, a reload signal is sent to the process after activating the configuration file.



96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/potluck/nginx.rb', line 96

def start
  return unless manage?

  @ssl&.ensure_files
  ensure_host_entries if @ensure_host_entries
  ensure_include

  write_config
  activate_config

  run('nginx -t')

  status == :active ? reload : super
end

#stop(hard = false) ⇒ Object

Ensures this instance’s configuration file is inactive and optionally stops the Nginx process if it’s managed.

  • hard - True if the Nginx process should be stopped, false to just inactivate this instance’s configuration file and leave Nginx running (optional, default: false).



118
119
120
121
122
123
124
# File 'lib/potluck/nginx.rb', line 118

def stop(hard = false)
  return unless manage?

  deactivate_config

  hard || status != :active ? super() : reload
end