potluck-nginx 0.0.7 → 0.0.8
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
- data/VERSION +1 -1
- data/lib/potluck/nginx/nginx_config.rb +388 -0
- data/lib/potluck/nginx/version.rb +1 -1
- data/lib/potluck/nginx.rb +483 -283
- metadata +28 -25
- data/lib/potluck/nginx/ssl.rb +0 -134
- data/lib/potluck/nginx/util.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0dcb1ed538b28f2b282437d29431bb42fb10990a90a19e2e727d92afa5b0bf90
|
4
|
+
data.tar.gz: 1fb1479069a6338705dd54715730305fbc1d7c77100e469fca9f17b929afd5d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f36dba8e9127d134450841b88037ddca7daca64beb7cc20ed4ed49d271addb1e898d15125d6377a30f324415e6984f7325c5627b5e3398cecddf0b84b1efaa4
|
7
|
+
data.tar.gz: 82539b84346b059ba1e65b9a61e3e29e66c2b58897c12d96bc9a4fed54024545b7e783ce0eaea385ad406071fea606108cabf144b19fa8bf03cd06b47d05abb4
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.8
|
@@ -0,0 +1,388 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require('delegate')
|
4
|
+
|
5
|
+
module Potluck
|
6
|
+
class Nginx < Service
|
7
|
+
# Public: Class for building and generating Nginx config file content. An instance of this class is
|
8
|
+
# passed to the block given to Nginx.new and Nginx#config.
|
9
|
+
#
|
10
|
+
# The name NginxConfig was chosen over Config because this is specifically for generating an Nginx
|
11
|
+
# config file--it is not for configuring the Potluck::Nginx class or an instance of it.
|
12
|
+
#
|
13
|
+
# Examples
|
14
|
+
#
|
15
|
+
# config = NginxConfig.new do |c|
|
16
|
+
# c.server do
|
17
|
+
# c.access_log('/path/to/access.log')
|
18
|
+
# c.add_header('X-Greeting', "'hello' always")
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # Add more configuration.
|
23
|
+
# config.modify do |c|
|
24
|
+
# c.server do
|
25
|
+
# c.add_header('X-Subject', "'world' always")
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
class NginxConfig
|
29
|
+
# Internal: Wrapper for config values that should be overwritten when set again (by default, repeat
|
30
|
+
# directives are appended to the config--they do not replace any previous occurrence). Used by
|
31
|
+
# NginxConfig#add_directive when passed soft: true.
|
32
|
+
class SoftValue < SimpleDelegator
|
33
|
+
end
|
34
|
+
|
35
|
+
# Internal: Regex for matching raw text items in the config hash. Raw text that gets added at various
|
36
|
+
# points uses an incrementing Symbol key of the form :"raw[0]", :"raw[1]", :"raw[2]", ....
|
37
|
+
RAW_KEY_REGEX = /^raw\[(?<index>\d+)\]$/
|
38
|
+
|
39
|
+
# Public: Create a new instance.
|
40
|
+
#
|
41
|
+
# block - Block passed to #modify for defining the config.
|
42
|
+
def initialize(&block)
|
43
|
+
@config = {}
|
44
|
+
@context = []
|
45
|
+
|
46
|
+
modify(&block) if block
|
47
|
+
end
|
48
|
+
|
49
|
+
# Public: Modify this config.
|
50
|
+
#
|
51
|
+
# block - Block to execute for modifying the config. Self is passed to the block, accepting any method
|
52
|
+
# method called on it and transforming it into an Nginx configuration directive.
|
53
|
+
#
|
54
|
+
# Examples
|
55
|
+
#
|
56
|
+
# config = NginxConfig.new
|
57
|
+
#
|
58
|
+
# config.modify do |c|
|
59
|
+
# c.server do
|
60
|
+
# c.listen('80')
|
61
|
+
# c.listen('[::]:80')
|
62
|
+
# c.server_names('hello.world', 'hi.there')
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# Returns self.
|
67
|
+
def modify(&block)
|
68
|
+
block&.call(self)
|
69
|
+
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
# Public: Append Nginx config content via raw text or hash content.
|
74
|
+
#
|
75
|
+
# content - String or Hash of Nginx config content (any other type is gracefully ignored). See
|
76
|
+
# #add_hash and #add_raw.
|
77
|
+
#
|
78
|
+
# Returns self.
|
79
|
+
def <<(content)
|
80
|
+
case content
|
81
|
+
when Hash
|
82
|
+
add_hash(content)
|
83
|
+
when String
|
84
|
+
add_raw(content)
|
85
|
+
end
|
86
|
+
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
# Public: Add an Nginx directive. See #add_block_directive and #add_directive.
|
91
|
+
#
|
92
|
+
# name - Symbol name of the directive.
|
93
|
+
# args - Zero or more Object directive values.
|
94
|
+
# kwargs - Hash of optional meta information for defining the directive.
|
95
|
+
# block - Block that adds child directives.
|
96
|
+
#
|
97
|
+
# Returns nil.
|
98
|
+
# Raises NoMethodError if the named method is a defined private method (this replicates standard
|
99
|
+
# behavior for private methods).
|
100
|
+
def method_missing(name, *args, **kwargs, &block)
|
101
|
+
if private_method?(name)
|
102
|
+
raise(NoMethodError, "private method '#{name}' called for an instance of #{self.class.name}")
|
103
|
+
end
|
104
|
+
|
105
|
+
if block
|
106
|
+
add_block_directive(name, *args, &block)
|
107
|
+
elsif !args.empty?
|
108
|
+
add_directive(name, *args, **kwargs)
|
109
|
+
end
|
110
|
+
|
111
|
+
nil
|
112
|
+
end
|
113
|
+
|
114
|
+
# Public: Determine if this instance handles a particular method call.
|
115
|
+
#
|
116
|
+
# name - Symbol name of the method.
|
117
|
+
# include_private - Boolean indicating if private methods should be included in the check.
|
118
|
+
#
|
119
|
+
# Returns false if the method is private and include_private is false, and true otherwise (since
|
120
|
+
# #method_missing is implemented and accepts any method name).
|
121
|
+
def respond_to_missing?(name, include_private = false)
|
122
|
+
private_method?(name) ? include_private : true
|
123
|
+
end
|
124
|
+
|
125
|
+
# Public: Generate the Nginx config file content.
|
126
|
+
#
|
127
|
+
# Returns the String content.
|
128
|
+
def to_s
|
129
|
+
to_nginx_config(@config)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Public: Get the value of a directive.
|
133
|
+
#
|
134
|
+
# keys - One or more String directive names, Symbol raw keys (e.g. :"raw[0]"), or Integer array
|
135
|
+
# indexes.
|
136
|
+
#
|
137
|
+
# Returns the String, Array, or Hash directive value.
|
138
|
+
def dig(*keys)
|
139
|
+
@config.dig(*keys)
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
# Internal: Transform a hash into raw content for an Nginx configuration file.
|
145
|
+
#
|
146
|
+
# hash - Hash config definition.
|
147
|
+
# indent: - Integer number of spaces to indent (used when the method is called recursively and should
|
148
|
+
# not be set explicitly).
|
149
|
+
#
|
150
|
+
# Returns the String content.
|
151
|
+
def to_nginx_config(hash, indent: 0)
|
152
|
+
hash.each.with_object(+'') do |(name, items), str|
|
153
|
+
if name.kind_of?(Symbol) && name.match?(RAW_KEY_REGEX)
|
154
|
+
str << items.gsub(/^(?=.)/, ' ' * indent)
|
155
|
+
str << "\n" unless str.end_with?("\n")
|
156
|
+
|
157
|
+
next
|
158
|
+
end
|
159
|
+
|
160
|
+
items.each do |item|
|
161
|
+
if item.kind_of?(Hash)
|
162
|
+
str << "#{' ' * indent}#{name} {\n" \
|
163
|
+
"#{to_nginx_config(item, indent: indent + 2)}" \
|
164
|
+
"#{' ' * indent}}\n"
|
165
|
+
else
|
166
|
+
str << "#{' ' * indent}#{name} #{item};\n"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Internal: Get the hash for the current context stack.
|
173
|
+
#
|
174
|
+
# Returns the contextual Hash.
|
175
|
+
def contextual_config
|
176
|
+
@context.empty? ? @config : @config.dig(*@context)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Internal: Add an Nginx block directive.
|
180
|
+
#
|
181
|
+
# name - String or Symbol name of the block directive.
|
182
|
+
# args - Zero or more Object values that are converted to strings (via #to_s) and joined to name with
|
183
|
+
# spaces; an optional last Integer item specifies the index into the array of directive values
|
184
|
+
# (used when there is more than one of the same directive, such as with server blocks).
|
185
|
+
# block - Block that defines child directives.
|
186
|
+
#
|
187
|
+
# Examples
|
188
|
+
#
|
189
|
+
# add_block_directive('server') do |c|
|
190
|
+
# c.listen('8080')
|
191
|
+
# end
|
192
|
+
#
|
193
|
+
# add_block_directive('server', 1) do |c|
|
194
|
+
# c.listen('4433')
|
195
|
+
# c.server_name('hi.there')
|
196
|
+
# end
|
197
|
+
#
|
198
|
+
# add_block_directive('server', 0) do |c|
|
199
|
+
# c.server_name('hello.world')
|
200
|
+
# end
|
201
|
+
#
|
202
|
+
# to_s
|
203
|
+
#
|
204
|
+
# # => "server {\n" +
|
205
|
+
# # " listen 8080;\n"
|
206
|
+
# # " server_name hello.world;\n"
|
207
|
+
# # "}\n"
|
208
|
+
# # "server {\n" +
|
209
|
+
# # " listen 4433;\n"
|
210
|
+
# # " server_name hi.there;\n"
|
211
|
+
# # "}\n"
|
212
|
+
#
|
213
|
+
# Returns nothing.
|
214
|
+
def add_block_directive(name, *args, &block)
|
215
|
+
index = args.pop if args.last.kind_of?(Integer)
|
216
|
+
context = "#{name} #{args.join(' ')}".strip
|
217
|
+
directives = (contextual_config[context] ||= [])
|
218
|
+
index ||= directives.size - 1
|
219
|
+
|
220
|
+
unless directives[index]
|
221
|
+
directives << {}
|
222
|
+
index = directives.size - 1
|
223
|
+
end
|
224
|
+
|
225
|
+
@context << context << index
|
226
|
+
|
227
|
+
block.call(self)
|
228
|
+
|
229
|
+
@context.pop(2)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Internal: Add an Nginx (non-block) directive.
|
233
|
+
#
|
234
|
+
# Repeated calls with the same name accumulate into an array. If there are two or more args,
|
235
|
+
# subsequent calls with the same name and same first arg will overwrite the original value for that
|
236
|
+
# name and first arg (see the examples if this is unclear).
|
237
|
+
#
|
238
|
+
# If args is empty or only contains nil and/or empty string values, any previously-added directive
|
239
|
+
# with the same name is removed.
|
240
|
+
#
|
241
|
+
# If soft: true is passed, value is added but treated as 'soft'. Soft values are all removed as soon
|
242
|
+
# as a non-soft value is added. This is used to set up default values that can be optionally
|
243
|
+
# overwritten.
|
244
|
+
#
|
245
|
+
# name - String or Symbol name of the directive.
|
246
|
+
# args - Zero or more Object values that are converted to strings (via #to_s) and joined to each
|
247
|
+
# other with spaces.
|
248
|
+
# kwargs - Hash of optional meta information. Only :soft is used. Any others will be ignored.
|
249
|
+
#
|
250
|
+
# Examples
|
251
|
+
#
|
252
|
+
# add_directive('add_header', 'Referrer-Policy', "'same-origin' always")
|
253
|
+
# add_directive('add_header', 'X-Frame-Options', "'SAMEORIGIN' always")
|
254
|
+
# add_directive('add_header', 'X-Frame-Options', "'DENY' always")
|
255
|
+
# to_s
|
256
|
+
#
|
257
|
+
# # => "add_header Referrer-Policy 'same-origin' always;\n" +
|
258
|
+
# # "add_header X-Frame-Options 'DENY' always;\n"
|
259
|
+
#
|
260
|
+
# add_directive('add_header', 'Referrer-Policy', nil)
|
261
|
+
# to_s
|
262
|
+
#
|
263
|
+
# # => "add_header X-Frame-Options 'DENY' always;\n"
|
264
|
+
#
|
265
|
+
# access_log('/first/path/to/access.log', soft: true)
|
266
|
+
# access_log('/second/path/to/access.log', soft: true)
|
267
|
+
# to_s
|
268
|
+
#
|
269
|
+
# # => "access_log /first/path/to/access.log;\n" +
|
270
|
+
# # "access_log /second/path/to/access.log;\n"
|
271
|
+
#
|
272
|
+
# access_log('/third/path/to/access.log')
|
273
|
+
# to_s
|
274
|
+
#
|
275
|
+
# # => "access_log /third/path/to/access.log;\n"
|
276
|
+
#
|
277
|
+
# Returns nothing.
|
278
|
+
def add_directive(name, *args, **kwargs)
|
279
|
+
name = name.to_s
|
280
|
+
directive = (contextual_config[name] ||= [])
|
281
|
+
soft = kwargs[:soft]
|
282
|
+
|
283
|
+
key = "#{args.first} " if args.size >= 2
|
284
|
+
value = args.join(' ').strip
|
285
|
+
value = nil if value.empty? || value == key&.strip
|
286
|
+
value = SoftValue.new(value) if soft
|
287
|
+
|
288
|
+
unless soft
|
289
|
+
directive.reject! do |item|
|
290
|
+
item.kind_of?(SoftValue)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
if key
|
295
|
+
index = directive.index do |item|
|
296
|
+
item.start_with?(key)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
if index
|
301
|
+
value.nil? ? directive.delete_at(index) : (directive[index] = value)
|
302
|
+
elsif value.nil?
|
303
|
+
directive.clear
|
304
|
+
else
|
305
|
+
directive << value
|
306
|
+
end
|
307
|
+
|
308
|
+
contextual_config.delete(name) if directive.empty?
|
309
|
+
end
|
310
|
+
|
311
|
+
# Internal: Add Nginx config content using a hash.
|
312
|
+
#
|
313
|
+
# hash - Hash of config content. Keys are String directive names or the Symbol :raw or :raw[<i>]
|
314
|
+
# (where <i> is an Integer). Values are a Hash for block directive content, an Array of
|
315
|
+
# Object--each of which will have #to_s called on it--for repeated directives, or an Object
|
316
|
+
# which will have #to_s called on it.
|
317
|
+
#
|
318
|
+
# Examples
|
319
|
+
#
|
320
|
+
# add_hash({
|
321
|
+
# 'server' => {
|
322
|
+
# 'access_log' => '/path/to/access.log',
|
323
|
+
# 'add_header' => [
|
324
|
+
# "Referrer-Policy 'same-origin' always",
|
325
|
+
# "X-Frame-Options 'DENY' always",
|
326
|
+
# ],
|
327
|
+
# 'raw[0]': 'return 404',
|
328
|
+
# },
|
329
|
+
# })
|
330
|
+
#
|
331
|
+
# Returns nothing.
|
332
|
+
def add_hash(hash)
|
333
|
+
hash.each do |key, value|
|
334
|
+
if key == :raw || key.match?(RAW_KEY_REGEX)
|
335
|
+
add_raw(value)
|
336
|
+
next
|
337
|
+
end
|
338
|
+
|
339
|
+
case value
|
340
|
+
when Hash
|
341
|
+
add_block_directive(key) do
|
342
|
+
add_hash(value)
|
343
|
+
end
|
344
|
+
when Array
|
345
|
+
value.each do |item|
|
346
|
+
add_directive(key, item)
|
347
|
+
end
|
348
|
+
else
|
349
|
+
add_directive(key, value)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
# Internal: Add raw Nginx config content using a string.
|
355
|
+
#
|
356
|
+
# content - String config content.
|
357
|
+
#
|
358
|
+
# Returns nothing.
|
359
|
+
def add_raw(content)
|
360
|
+
contextual_config[next_raw_key] = content
|
361
|
+
end
|
362
|
+
|
363
|
+
# Internal: Get the next hash key used for raw content. If the current context has, say, keys :raw[0]
|
364
|
+
# and :raw[1], then :raw[2] will be returned.
|
365
|
+
#
|
366
|
+
# Returns the Symbol key.
|
367
|
+
def next_raw_key
|
368
|
+
index = contextual_config
|
369
|
+
.keys
|
370
|
+
.grep(RAW_KEY_REGEX)
|
371
|
+
.map { |k| k[RAW_KEY_REGEX, :index].to_i }
|
372
|
+
.last
|
373
|
+
|
374
|
+
:"raw[#{(index || -1) + 1}]"
|
375
|
+
end
|
376
|
+
|
377
|
+
# Internal: Determine if a method is defined and is private.
|
378
|
+
#
|
379
|
+
# name - Symbol name of the method.
|
380
|
+
#
|
381
|
+
# Returns the boolean result.
|
382
|
+
def private_method?(name)
|
383
|
+
@@private_methods ||= self.class.private_instance_methods(false)
|
384
|
+
@@private_methods.include?(name)
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|