web_server_config_generator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
Binary file
@@ -0,0 +1,513 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fileutils'
4
+ require 'pp'
5
+ require 'find'
6
+ require 'digest/sha1'
7
+ require 'pathname'
8
+ require 'rubygems'
9
+ require 'highline/import'
10
+ require 'optparse'
11
+ require 'yaml'
12
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
13
+ require 'web_server_config_generator'
14
+ require 'web_server_config_generator/project_directory'
15
+ require 'web_server_config_generator/sub_uri_project'
16
+
17
+
18
+ $ENVS = []
19
+ $TEST_MODE = false
20
+ $CREATE_WEB_SERVER_FILES_DIR = false
21
+ $PRINT_HOSTS = false
22
+ $WEB_SERVER_FILES_DIR = nil
23
+ $ADD_HOSTS = nil
24
+ $RESTART_NGINX = nil
25
+
26
+ opts = OptionParser.new do |opts|
27
+ opts.banner = "Usage: #{File.basename(__FILE__)} [project(s) dir] [options]"
28
+
29
+ opts.separator ""
30
+ opts.separator "This will generate web server configuration files for your projects."
31
+ opts.separator ""
32
+ opts.separator "If you supply a project directory we assume you have run this before and will just generate the files for that project. If you specify your projects directory we will generate the files for all projects found. Not supplying a directory is the same as supplying your current directory."
33
+ opts.separator ""
34
+ opts.separator "No flags = try to generate files for all envs"
35
+ opts.separator ""
36
+ opts.separator "Specific options:"
37
+
38
+ # Mandatory argument.
39
+ opts.on("-e", "--environment ENV",
40
+ "specify a specific environment to generate (defaults to all in database.yml)") do |env|
41
+ $ENVS << env
42
+ end
43
+
44
+ opts.on("-n", "--list-hosts",
45
+ "list generated hostnames, useful for setting up the hosts file on your own") do
46
+ $PRINT_HOSTS = true
47
+ end
48
+
49
+ opts.on("-c", "--create-web-server-files-dir",
50
+ "create web_server_files directory (useful the first time you run this script) in this case the supplied (or assumed) directory will be set as the 'projects' directory") do
51
+ $CREATE_WEB_SERVER_FILES_DIR = true
52
+ end
53
+
54
+ opts.on("-p", "--projects-dir DIR",
55
+ "specify the directory containing all your projects") do |dir|
56
+ $PROJECTS_DIRECTORY = dir
57
+ end
58
+
59
+ opts.on("-l", "--web-server-files-dir-location DIR",
60
+ "put all the web server configuration files in DIR instead of the default location") do |path|
61
+ $WEB_SERVER_FILES_DIR = File.expand_path(path)
62
+ end
63
+
64
+ opts.on("-a", "--[no-]add-hosts",
65
+ "add ghost entries for generated hostnames, requires ghost gem") do |b|
66
+ $ADD_HOSTS = b
67
+ end
68
+
69
+ opts.on("-r", "--[no-]restart-nginx",
70
+ "restart nginx at the end") do |b|
71
+ $RESTART_NGINX = b
72
+ end
73
+
74
+ opts.on("-v", "--verbose",
75
+ "verbose") do
76
+ $VERBOSE = true
77
+ end
78
+
79
+ opts.on("-t", "--test-mode",
80
+ "test mode; do not modify the FS, just print messsages") do
81
+ $TEST_MODE = true
82
+ end
83
+
84
+ # # Optional argument; multi-line description.
85
+ # opts.on("-i", "--inplace [EXTENSION]",
86
+ # "Edit ARGV files in place",
87
+ # " (make backup if EXTENSION supplied)") do |ext|
88
+ # options.inplace = true
89
+ # options.extension = ext || ''
90
+ # options.extension.sub!(/\A\.?(?=.)/, ".") # Ensure extension begins with dot.
91
+ # end
92
+
93
+ # # Boolean switch.
94
+ # opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
95
+ # options.verbose = v
96
+ # end
97
+
98
+ opts.separator ""
99
+ opts.separator "Common options:"
100
+
101
+ # No argument, shows at tail. This will print an options summary.
102
+ # Try it and see!
103
+ opts.on_tail("-h", "--help", "Show this message") do
104
+ puts opts
105
+ exit
106
+ end
107
+ end
108
+
109
+ opts.parse!(ARGV)
110
+
111
+ def agree( yes_or_no_question, character = nil )
112
+ ask(yes_or_no_question, lambda { |yn| yn.downcase[0] == ?y}) do |q|
113
+ q.validate = /\Ay(?:es)?|no?\Z/i
114
+ q.responses[:not_valid] = 'Please enter "yes" or "no".'
115
+ q.responses[:ask_on_error] = :question
116
+ q.character = character
117
+ yield q if block_given?
118
+ end
119
+ end
120
+
121
+ module WebServerSetup
122
+ WEB_SERVER_FILES_DIR_NAME = ".webconfig"
123
+
124
+ class Directory < Pathname
125
+ def +(other)
126
+ other = self.class.new(other) unless self.class === other
127
+ self.class.new(plus(@path, other.to_s))
128
+ end
129
+
130
+ def mkpath
131
+ if $TEST_MODE
132
+ puts "test mode: mkpath #{self}"
133
+ else
134
+ super
135
+ end
136
+ end
137
+
138
+ def write(arg)
139
+ if $TEST_MODE
140
+ puts "test mode: write #{self}, #{arg.to_s[0, 100]}..."
141
+ else
142
+ FileUtils.mkdir_p File.dirname(self)
143
+ self.open("w") { |f| f.write arg }
144
+ end
145
+ end
146
+
147
+ def read
148
+ if self.exist?
149
+ self.open("r") { |f| f.read }
150
+ else
151
+ ""
152
+ end
153
+ end
154
+ end
155
+
156
+ class Generator
157
+ def initialize(options)
158
+ options.each do |opt, val|
159
+ instance_variable_set("@#{opt}", val)
160
+ end
161
+
162
+ @starting_port = options[:starting_port] || 40000
163
+ @port_pool_size = options[:port_pool_size] || 10000
164
+
165
+ @environment_map = Hash.new(options[:environments]) if options[:environments]
166
+
167
+ setup_config_dir
168
+
169
+ @project_or_projects_dir ||= projects_dir
170
+ end
171
+
172
+ def write_conf_files
173
+ FileUtils.cd projects_dir do
174
+ # generate files for each proj/env
175
+ list_of_conf_files = []
176
+ app_projects.each do |p|
177
+ environment_map[p].each do |env|
178
+ list_of_conf_files << write_conf_file(p, env)
179
+ end
180
+ end
181
+
182
+ sub_uri_projects.each do |p|
183
+ create_sub_uri_links_dir p
184
+ list_of_conf_files << write_conf_file(p, p.env)
185
+ end
186
+
187
+ current_lines = []
188
+ web_server_vhost_nginx_conf.read.each_line { |l| current_lines << l }
189
+ new_lines = current_lines + list_of_conf_files.map { |p| "include #{p};\n" }
190
+ new_lines.uniq!
191
+ new_lines.sort!
192
+ web_server_vhost_nginx_conf.write(new_lines.join)
193
+ end
194
+ end
195
+
196
+ def create_sub_uri_links_dir(sub_uri_project)
197
+ env = sub_uri_project.env
198
+
199
+ web_server_sub_uri_apps_dir.mkdir unless web_server_sub_uri_apps_dir.exist?
200
+ links_dir = web_server_sub_uri_apps_dir + sub_uri_project.server_name
201
+ links_dir.mkdir unless links_dir.exist?
202
+ sub_uri_project.projects.each do |p|
203
+ symlink p.expand_path + "public", File.join(links_dir, p.relative_root_url_for_env(env))
204
+ if p.relative_root_url_root_for_env(env)
205
+ symlink p.expand_path + "public", File.join(links_dir, "root")
206
+ end
207
+ end
208
+ end
209
+
210
+ def server_names
211
+ projects.map { |p| p.server_names }.flatten
212
+ end
213
+
214
+ def add_ghost_entries
215
+ current_hosts = Host.list
216
+ already_correct = []
217
+ added = []
218
+ present_but_incorrect = []
219
+ server_names.each do |server_name|
220
+ if host = current_hosts.detect { |h| h.name == server_name }
221
+ if host.ip == "127.0.0.1"
222
+ already_correct << host
223
+ else
224
+ present_but_incorrect << host
225
+ end
226
+ else
227
+ if $TEST_MODE
228
+ puts "would have added #{server_name} -> 127.0.0.1"
229
+ else
230
+ added << Host.add(server_name)
231
+ end
232
+ end
233
+ end
234
+
235
+ if already_correct.size > 0
236
+ puts "\n#{already_correct.size} hosts were already setup correctly"
237
+ puts
238
+ end
239
+
240
+ if added.size > 0
241
+ puts "The following hostnames were added for 127.0.0.1:"
242
+ puts added.map { |h| " #{h.name}\n" }
243
+ puts
244
+ end
245
+
246
+ if present_but_incorrect.size > 0
247
+ puts "The following hostnames were present, but didn't map to 127.0.0.1:"
248
+ pad = present_but_incorrect.max{|a,b| a.to_s.length <=> b.to_s.length }.to_s.length
249
+ puts present_but_incorrect.map { |h| "#{h.name.rjust(pad+2)} -> #{h.ip}\n" }
250
+ puts
251
+ end
252
+ end
253
+
254
+ def setup_config_dir
255
+ unless File.exist?(config_dir)
256
+ unless dir = $PROJECTS_DIRECTORY
257
+ puts "It looks like this is the first time you've run me."
258
+ puts
259
+ if agree("setup #{@project_or_projects_dir} as your projects dir? [Y/n]") { |q| q.default = "Y"}
260
+ dir = @project_or_projects_dir
261
+ else
262
+ puts "for your first run you'll need to supply your projects directory"
263
+ exit
264
+ end
265
+ puts "setting up config dir"
266
+ end
267
+
268
+ FileUtils.mkdir_p config_dir
269
+ initial_config = { :projects_dirs => [File.expand_path(dir)]}
270
+ save_global_config(initial_config)
271
+ end
272
+ end
273
+
274
+ def symlink(link_target, link_name)
275
+ if File.exist?(link_name)
276
+ if FileTest.symlink?(link_name)
277
+ unless File.readlink(link_name) == link_target
278
+ puts "symlink '#{link_name}' exists, but doesn't appear to link to the correct place"
279
+ end
280
+ else
281
+ puts "couldn't make symlink '#{link_name}', something's already there"
282
+ end
283
+ else
284
+ File.symlink link_target, link_name
285
+ end
286
+ end
287
+
288
+ def setup_webserver_links_dir
289
+ FileUtils.cd projects_dir do
290
+ link_target = File.join "..", ".."
291
+ web_server_links_dir.mkpath
292
+ environments.each do |e|
293
+ link_name = web_server_links_dir + e
294
+ symlink(link_target, link_name)
295
+ end
296
+ end
297
+ end
298
+
299
+ def app_projects
300
+ @app_projects ||=
301
+ begin
302
+ find_project_and_symlink_dirs
303
+ @app_projects
304
+ end
305
+ end
306
+
307
+ def sub_uri_projects
308
+ return @sub_uri_projects if @sub_uri_projects
309
+ server_name_env_project_map = {}
310
+ app_projects.each do |p|
311
+ p.server_name_env_pairs.each do |server_name, env|
312
+ server_name_env_project_map[server_name] ||= {}
313
+ server_name_env_project_map[server_name][:env] ||= env
314
+ raise "can't have one hostname map to multiple envs" if server_name_env_project_map[server_name][:env] != env
315
+ server_name_env_project_map[server_name][:projects] ||= []
316
+ server_name_env_project_map[server_name][:projects] << p
317
+ end
318
+ end
319
+ server_name_env_project_map = server_name_env_project_map.select { |server_name, env_and_projects| env_and_projects[:projects].size > 1 }
320
+ @sub_uri_projects = server_name_env_project_map.map do |server_name, env_and_projects|
321
+ WebServerConfigGenerator::SubUriProject.new(server_name, env_and_projects, self)
322
+ end
323
+ end
324
+
325
+ def projects
326
+ app_projects + sub_uri_projects
327
+ end
328
+
329
+ def environments
330
+ @environments ||= environment_map.values.flatten.uniq
331
+ end
332
+
333
+ def web_server_vhost_nginx_conf
334
+ web_server_vhost_nginx_dir + "projects.conf"
335
+ end
336
+
337
+ def check_nginx_conf
338
+ unless nginx_conf =~ /include.*#{web_server_vhost_nginx_conf}/
339
+ puts "\nWarning: You'll need to make sure this line is in your nginx config, in the http block:"
340
+ puts " include #{web_server_vhost_nginx_conf};"
341
+ end
342
+
343
+ unless nginx_conf =~ /server_names_hash_bucket_size.*128/
344
+ puts "\nWarning: Couldn't find the following line in your nginx conf. It should be in the http block."
345
+ puts " server_names_hash_bucket_size 128;"
346
+ end
347
+ end
348
+
349
+ def prompt_to_restart_nginx
350
+ puts
351
+ if $RESTART_NGINX || ($RESTART_NGINX.nil? && agree("Restart nginx? [Y/n]") { |q| q.default = "Y"})
352
+ puts "Restarting nginx..."
353
+ cmd = "sudo killall nginx; sleep 1 && sudo #{nginx}"
354
+ puts "running: #{cmd}"
355
+ system cmd
356
+ end
357
+ end
358
+
359
+ def web_server_links_dir
360
+ web_server_files_dir + "links"
361
+ end
362
+
363
+ def projects_dir
364
+ WebServerSetup::Directory.new(global_config[:projects_dirs].first).expand_path
365
+ end
366
+
367
+ def web_server_sub_uri_apps_dir
368
+ web_server_files_dir + "sub_uri_apps"
369
+ end
370
+
371
+ private
372
+
373
+ def config_dir
374
+ $WEB_SERVER_FILES_DIR || begin
375
+ raise "Couldn't fine $HOME" unless home_dir = ENV["HOME"]
376
+ File.join(home_dir, ".webconfig")
377
+ end
378
+ end
379
+
380
+ def global_config(reload = false)
381
+ @global_config = nil if reload
382
+ @global_config ||= YAML.load_file(global_config_path)
383
+ end
384
+
385
+ def save_global_config(config)
386
+ @global_config = config
387
+ File.open(global_config_path, "w") { |f| f.write config.to_yaml }
388
+ end
389
+
390
+ def global_config_path
391
+ File.join(config_dir, "global_config.yml")
392
+ end
393
+
394
+ def web_server_files_dir
395
+ $WEB_SERVER_FILES_DIR ? Directory.new($WEB_SERVER_FILES_DIR) : config_dir
396
+ end
397
+
398
+ def web_server_vhost_dir
399
+ web_server_files_dir + "vhost"
400
+ end
401
+
402
+ def web_server_vhost_nginx_dir
403
+ web_server_vhost_dir + "nginx"
404
+ end
405
+
406
+ def find_project_and_symlink_dirs
407
+ @app_projects ||= []
408
+
409
+ if @project_or_projects_dir.expand_path != projects_dir.expand_path
410
+ @app_projects = [WebServerConfigGenerator::ProjectDirectory.new(@project_or_projects_dir, self)]
411
+ return
412
+ end
413
+
414
+ Dir[File.join(projects_dir, "**", ".webconfig.yml")].each do |webconfig_path|
415
+ @app_projects << WebServerConfigGenerator::ProjectDirectory.new(File.dirname(webconfig_path), self)
416
+ end
417
+ end
418
+
419
+ def projects_environment_map
420
+ app_projects.inject({}) { |map, p| map[p] = p.environments; map }
421
+ end
422
+
423
+ def symlinks_environment_map
424
+ sub_uri_projects.inject({}) { |map, p| map[p] = p.env.to_s; map }
425
+ end
426
+
427
+ def environment_map
428
+ @environment_map ||= projects_environment_map.merge(symlinks_environment_map)
429
+ end
430
+
431
+ def projects_relative_project_path(dir)
432
+ File.expand_path(dir).sub(File.expand_path(projects_dir), '').sub(/^\//, '')
433
+ end
434
+
435
+ def write_conf_file(p, env)
436
+ project_vhost_dir = web_server_vhost_nginx_dir + projects_relative_project_path(p)
437
+ project_vhost_dir.mkpath
438
+ project_env_vhost_filename = project_vhost_dir + "#{env}.conf"
439
+ new_contents = p.generate_conf_file_contents(:env => env)
440
+ if project_env_vhost_filename.exist?
441
+ old_contents = project_env_vhost_filename.read
442
+ if old_contents != new_contents
443
+ puts "#{project_env_vhost_filename} exists, but doesn't match"
444
+ end
445
+ else
446
+ project_env_vhost_filename.write new_contents
447
+ end
448
+ project_env_vhost_filename.expand_path
449
+ end
450
+
451
+ def nginx_conf_path
452
+ m = `#{nginx} -t 2>&1`.match /the configuration file (.*) syntax is ok/
453
+ m[1]
454
+ end
455
+
456
+ def nginx_conf
457
+ @nginx_conf ||= begin
458
+ File.read(nginx_conf_path)
459
+ rescue Exception => e
460
+ puts "Warning: Couldn't find/read nginx conf"
461
+ ""
462
+ end
463
+ end
464
+
465
+ def nginx
466
+ nginx_path_options = [
467
+ "nginx",
468
+ "/opt/nginx/sbin/nginx"
469
+ ]
470
+ nginx_path_options.detect { |p| system "which #{p} &> /dev/null" }
471
+ end
472
+
473
+ end
474
+
475
+ end
476
+
477
+
478
+ project_or_projects_dir = ARGV.first && WebServerSetup::Directory.new(File.expand_path(ARGV.first))
479
+
480
+ options = {}
481
+ options[:environments] = $ENVS if $ENVS.any?
482
+ options[:project_or_projects_dir] = project_or_projects_dir
483
+ web_server_setup = WebServerSetup::Generator.new(options)
484
+
485
+ web_server_setup.setup_webserver_links_dir
486
+
487
+ if $PRINT_HOSTS
488
+ puts web_server_setup.server_names.join("\n")
489
+ exit 0
490
+ end
491
+
492
+
493
+ web_server_setup.write_conf_files
494
+
495
+ begin
496
+ require 'ghost'
497
+ puts
498
+ if $ADD_HOSTS || ($ADD_HOSTS.nil? && agree("Setup ghost entries for projects? [Y/n]") { |q| q.default = "Y"})
499
+ puts "Setting up ghost entries..."
500
+ web_server_setup.add_ghost_entries
501
+ end
502
+ rescue LoadError
503
+ puts "Couldn't load ghost so I won't add hostname entries for you. Install the 'ghost' gem, or run me with a -n to get a list of hostnames to setup youself."
504
+ end
505
+
506
+ pp web_server_setup.app_projects if $VERBOSE
507
+ # pp web_server_setup.symlink_dirs if $VERBOSE
508
+ pp web_server_setup.environments if $VERBOSE
509
+
510
+ web_server_setup.check_nginx_conf
511
+
512
+ web_server_setup.prompt_to_restart_nginx
513
+
@@ -0,0 +1,8 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module WebServerConfigGenerator
5
+ VERSION = '0.0.1'
6
+ STARTING_PORT = 40_000
7
+ PORT_POOL_SIZE = 10_000
8
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: web_server_config_generator
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Expected Behavior
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-03-17 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: automatically generate web server config files for you projects directory
22
+ email:
23
+ executables:
24
+ - web_server_setup
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README
29
+ files:
30
+ - lib/web_server_config_generator.rb
31
+ - lib/web_server_config_generator/project_directory.rb
32
+ - lib/web_server_config_generator/sub_uri_project.rb
33
+ has_rdoc: true
34
+ homepage: http://github.com/expectedbehavior/web_server_config_generator
35
+ licenses: []
36
+
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.3.6
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: automatically generate web server config files for you projects directory
63
+ test_files: []
64
+