vhost_generator 0.1.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.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in vhost_generator.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Julien Pervillé
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # VhostGenerator
2
+
3
+ This gem outputs a general-purpose VirtualHost configuration file
4
+ to run your web application behind an nginx or apache frontend.
5
+
6
+ The motivation of this gem is to automate that tedious task the first time, but
7
+ also every time the virtualhost parameters (such as the number of instances to
8
+ run and the ports where the instances are listening).
9
+
10
+ The gem features tries to integrate with the [foreman][1] gem by:
11
+ * reading configuration parameters from `ENV` (or from the `.env` file if present)
12
+ * detecting whether to proxy to tcp or unix socket from `Procfile` if present
13
+ * reading configuration from foreman's standard environment variables to allow
14
+ for generating a virtualhost that matches the last `foreman export`.
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ gem 'vhost_generator'
21
+
22
+ And then execute:
23
+
24
+ $ bundle
25
+
26
+ Or install it yourself as:
27
+
28
+ $ gem install vhost_generator
29
+
30
+ ## Usage
31
+
32
+ Basic usage:
33
+
34
+ $ bundle exec vhost-generator -l 80 -s myapp.com -p 5000,5001,5002
35
+
36
+ Advanced usage: all command-line switches have their equivalent environment variables. See note in `bundle exec vhost-generator --help`.
37
+
38
+ $ SERVER_PORTS=80 SERVER_NAMES=myapp.com INSTANCE_PORTS=5000,5001,5002 bundle exec vhost-generator
39
+
40
+ Advanced usage for lazy people: environment variables may be saved into the `.env` file or into another file whose name is given in `ENV['DOTENV']`.
41
+ This last option is nice, as it allows to store the environment in a file and reuse it later to generate the same virtualhost configurations.
42
+
43
+ $ echo "SERVER_PORTS=80\nSERVER_NAMES=myapp.com\nINSTANCE_PORTS=5000,5001,5002" >> my.env
44
+ $ DOTENV=my.env bundle exec vhost-generator
45
+
46
+ More advanced usages: see `features/` directory or run:
47
+
48
+ $ bundle exec vhost-generator --help
49
+
50
+ ## Tips
51
+
52
+ Protip: pipe with `sudo tee` to save the configuration in your nginx sites-enabled directory.
53
+
54
+ $ bundle exec vhost-generator -l 80 -s myapp.com -p 5000,5001,5002 | sudo tee /etc/nginx/sites-enabled/myapp
55
+
56
+ Protip: run through `foreman run` to leverage your application's `.env` (DRY and handy when having a configured `RAILS_RELATIVE_URL_ROOT` for example)
57
+
58
+ $ echo RAILS_RELATIVE_URL_ROOT='/myapp' >> .env
59
+ $ bundle exec foreman run vhost-generator -l 80 -s myapp.com -p 5000,5001,5002
60
+
61
+ ## Contributing
62
+
63
+ 1. Fork it
64
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
65
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
66
+ 4. Push to the branch (`git push origin my-new-feature`)
67
+ 5. Create new Pull Request
68
+
69
+ [1]: https://github.com/ddollar/foreman "Foreman"
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require "cucumber/rake/task"
3
+ require 'rspec/core/rake_task'
4
+
5
+ Cucumber::Rake::Task.new(:features) do |t|
6
+ t.cucumber_opts = "features --format pretty"
7
+ end
8
+
9
+ RSpec::Core::RakeTask.new(:spec)
10
+
11
+ task :default => :spec
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + '/../lib') unless $:.include?(File.dirname(__FILE__) + '/../lib')
3
+
4
+ require 'vhost_generator'
5
+
6
+ VhostGenerator.application.run
@@ -0,0 +1,12 @@
1
+ Feature: Output help summary
2
+
3
+ In order to know how to invoke the program
4
+ As a user of the library
5
+ I want to display the program's help summary.
6
+
7
+ Scenario: output program help summary
8
+ When I run `bundle exec vhost-generator --help`
9
+ Then it should pass with:
10
+ """
11
+ Display this help message.
12
+ """
@@ -0,0 +1,171 @@
1
+ Feature: Output nginx configuration file
2
+
3
+ In order to run my application as an nginx virtualhost
4
+ As a user of the library
5
+ I want to output a nginx virtualhost configuration.
6
+
7
+ Scenario: using command-line options
8
+ When I run `bundle exec vhost-generator -g nginx -o upstream=myupstream -f html -l 80,81 -s localhost,my.server -p 5000,5001,5002 -r /myapp`
9
+ Then the output should match /FILE GENERATED BY.*EDIT AT YOUR OWN RISK/
10
+ And the output should contain:
11
+ """
12
+ upstream myupstream {
13
+ server localhost:5000 fail_timeout=0;
14
+ server localhost:5001 fail_timeout=0;
15
+ server localhost:5002 fail_timeout=0;
16
+ }
17
+ """
18
+ And the output should contain:
19
+ """
20
+ server {
21
+ listen 80;
22
+ listen 81;
23
+ """
24
+ And the output should contain:
25
+ """
26
+ server_name localhost, my.server;
27
+ """
28
+ And the output should match /root.*html;/
29
+ And the output should contain:
30
+ """
31
+ try_files $uri/index.html $uri @upstream;
32
+ location @upstream {
33
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
34
+ proxy_set_header X-Forwarded-Proto $scheme;
35
+ proxy_set_header Host $http_host;
36
+ proxy_redirect off;
37
+ proxy_pass http://myupstream;
38
+ }
39
+ """
40
+ And the output should contain:
41
+ """
42
+ location /myapp/assets {
43
+ gzip_static on; # to serve pre-gzipped version
44
+ expires 60d;
45
+ add_header Cache-Control public;
46
+ }
47
+ """
48
+ And the output should contain:
49
+ """
50
+ error_page 500 502 503 504 /500.html;
51
+ client_max_body_size 4G;
52
+ keepalive_timeout 10;
53
+ }
54
+ """
55
+
56
+ Scenario: using dotenv
57
+ Given a file named "tmpdir/my.env" with:
58
+ """
59
+ GENERATOR=nginx
60
+ GENERATOR_OPTIONS=upstream=myupstream
61
+ STATIC_FOLDER=html
62
+ SERVER_PORTS=80,81
63
+ SERVER_NAMES=localhost,my.server
64
+ INSTANCE_PORTS=5000,5001,5002
65
+ RAILS_RELATIVE_URL_ROOT=/myapp
66
+ """
67
+ When I cd to "tmpdir"
68
+ And I set env variable "DOTENV" to "my.env"
69
+ And I run `bundle exec vhost-generator`
70
+ Then the output should match /FILE GENERATED BY.*EDIT AT YOUR OWN RISK/
71
+ And the output should contain:
72
+ """
73
+ upstream myupstream {
74
+ server localhost:5000 fail_timeout=0;
75
+ server localhost:5001 fail_timeout=0;
76
+ server localhost:5002 fail_timeout=0;
77
+ }
78
+ """
79
+ And the output should contain:
80
+ """
81
+ server {
82
+ listen 80;
83
+ listen 81;
84
+ """
85
+ And the output should contain:
86
+ """
87
+ server_name localhost, my.server;
88
+ """
89
+ And the output should match /root.*html;/
90
+ And the output should contain:
91
+ """
92
+ try_files $uri/index.html $uri @upstream;
93
+ location @upstream {
94
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
95
+ proxy_set_header X-Forwarded-Proto $scheme;
96
+ proxy_set_header Host $http_host;
97
+ proxy_redirect off;
98
+ proxy_pass http://myupstream;
99
+ }
100
+ """
101
+ And the output should contain:
102
+ """
103
+ location /myapp/assets {
104
+ gzip_static on; # to serve pre-gzipped version
105
+ expires 60d;
106
+ add_header Cache-Control public;
107
+ }
108
+ """
109
+ And the output should contain:
110
+ """
111
+ error_page 500 502 503 504 /500.html;
112
+ client_max_body_size 4G;
113
+ keepalive_timeout 10;
114
+ }
115
+ """
116
+
117
+ Scenario: using environment variables
118
+ When I set env variable "GENERATOR" to "nginx"
119
+ And I set env variable "GENERATOR_OPTIONS" to "upstream=myupstream"
120
+ And I set env variable "STATIC_FOLDER" to "html"
121
+ And I set env variable "SERVER_PORTS" to "80,81"
122
+ And I set env variable "SERVER_NAMES" to "localhost,my.server"
123
+ And I set env variable "INSTANCE_PORTS" to "5000,5001,5002"
124
+ And I set env variable "RAILS_RELATIVE_URL_ROOT" to "/myapp"
125
+ And I run `bundle exec vhost-generator`
126
+ Then the output should match /FILE GENERATED BY.*EDIT AT YOUR OWN RISK/
127
+ And the output should contain:
128
+ """
129
+ upstream myupstream {
130
+ server localhost:5000 fail_timeout=0;
131
+ server localhost:5001 fail_timeout=0;
132
+ server localhost:5002 fail_timeout=0;
133
+ }
134
+ """
135
+ And the output should contain:
136
+ """
137
+ server {
138
+ listen 80;
139
+ listen 81;
140
+ """
141
+ And the output should contain:
142
+ """
143
+ server_name localhost, my.server;
144
+ """
145
+ And the output should match /root.*html;/
146
+ And the output should contain:
147
+ """
148
+ try_files $uri/index.html $uri @upstream;
149
+ location @upstream {
150
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
151
+ proxy_set_header X-Forwarded-Proto $scheme;
152
+ proxy_set_header Host $http_host;
153
+ proxy_redirect off;
154
+ proxy_pass http://myupstream;
155
+ }
156
+ """
157
+ And the output should contain:
158
+ """
159
+ location /myapp/assets {
160
+ gzip_static on; # to serve pre-gzipped version
161
+ expires 60d;
162
+ add_header Cache-Control public;
163
+ }
164
+ """
165
+ And the output should contain:
166
+ """
167
+ error_page 500 502 503 504 /500.html;
168
+ client_max_body_size 4G;
169
+ keepalive_timeout 10;
170
+ }
171
+ """
@@ -0,0 +1,3 @@
1
+ When /^I set env variable "(\w+)" to "([^"]*)"$/ do |var, value|
2
+ ENV[var] = value
3
+ end
@@ -0,0 +1 @@
1
+ require 'aruba/cucumber'
@@ -0,0 +1,12 @@
1
+ Feature: Output program version
2
+
3
+ In order to know if I am up to date
4
+ As a user of the library
5
+ I want to know the current version of vhost-generator.
6
+
7
+ Scenario: output program version
8
+ When I run `bundle exec vhost-generator --version`
9
+ Then it should pass with:
10
+ """
11
+ vhost-generator, version 0.1.0
12
+ """
@@ -0,0 +1,150 @@
1
+ require 'vhost_generator/version'
2
+ require 'vhost_generator/vhost_configuration'
3
+ require 'optparse'
4
+ require 'ostruct'
5
+ require 'dotenv'
6
+
7
+ module VhostGenerator
8
+
9
+ ###########################################################################
10
+ # VhostGenerator main application object. When invoking +vhost-generator+
11
+ # from the command line, a VhostGenerator::Application object is created
12
+ # and run.
13
+ #
14
+ class Application
15
+ attr_writer :config
16
+
17
+ def initialize(output_stream=$stdout)
18
+ @name = File.basename($0 || 'vhost-generator')
19
+ @output_stream = output_stream
20
+ end
21
+
22
+ # Run the VhostGenerator application.
23
+ def run
24
+ standard_exception_handling do
25
+ # load serialized environment variables from DOTENV files if present.
26
+ dotenvs = ['.env', ENV['DOTENV']]
27
+ dotenvs.compact.each { |f| Dotenv.load(f) }
28
+ handle_env(ENV)
29
+ handle_options(ARGV)
30
+ config.cmdline << ['cd', Dir.pwd]
31
+ config.cmdline << [$0] + ARGV
32
+ @output_stream.puts config.output
33
+ end
34
+ end
35
+
36
+ def config(configurator=VhostGenerator::VhostConfiguration)
37
+ @config ||= configurator.new # XXX
38
+ end
39
+
40
+ def handle_env(env)
41
+ if path = env['STATIC_FOLDER']
42
+ config.static_folder = path
43
+ end
44
+ if ports = env['SERVER_PORTS']
45
+ config.server_ports = ports
46
+ end
47
+ if names = env['SERVER_NAMES']
48
+ config.server_names = names
49
+ end
50
+ if ports = env['INSTANCE_PORTS']
51
+ config.instance_ports = ports
52
+ end
53
+ if root = env['RAILS_RELATIVE_URL_ROOT']
54
+ config.relative_root = root
55
+ end
56
+ if generator = env['GENERATOR']
57
+ config.generator = generator
58
+ end
59
+ if options = env['GENERATOR_OPTIONS']
60
+ config.generator_options = options
61
+ end
62
+ end
63
+
64
+ def handle_options(argv)
65
+ OptionParser.new do |opts|
66
+ opts.banner = "Usage: #{@name} [options]"
67
+
68
+ opts.separator ""
69
+ opts.separator ['Note: all command-line options below also exist as ' \
70
+ 'environment variables.', 'You may try to setenv all ' \
71
+ 'uppercase names in the rest of this summary, eg.',
72
+ '`export RAILS_RELATIVE_URL_ROOT=/myapp`.'].join($/)
73
+
74
+ opts.separator ""
75
+ opts.separator "Application options:"
76
+ application_options.each { |args| opts.on(*args) }
77
+
78
+ opts.separator ""
79
+ opts.separator "Generator options:"
80
+ generator_options.each { |args| opts.on(*args) }
81
+
82
+ opts.separator ""
83
+
84
+ opts.on_tail("-v", "--version", "Display the program version.") do
85
+ @output_stream.puts "#{@name}, version #{VhostGenerator::VERSION}"
86
+ exit(true)
87
+ end
88
+
89
+ opts.on_tail("-h", "--help", "-H", "Display this help message.") do
90
+ @output_stream.puts opts
91
+ exit(true)
92
+ end
93
+ end.parse(argv)
94
+ end
95
+
96
+ def application_options
97
+ [
98
+ ['-f', '--static-folder STATIC_FOLDER',
99
+ %q{Path of your application's static folder (e.g. public/)},
100
+ lambda { |value| config.static_folder = value }],
101
+ ['-l', '--listen SERVER_PORTS',
102
+ %q{Public ports to listen on (e.g. 80,81)},
103
+ lambda { |value| config.server_ports = value }],
104
+ ['-s', '--server-name SERVER_NAMES',
105
+ %q{Server names to listen on (e.g. localhost,example.com)},
106
+ lambda { |value| config.server_names = value }],
107
+ ['-p', '--instance-port INSTANCE_PORTS',
108
+ %q{Internal ports where instances listen on (e.g. 5000,5001)},
109
+ lambda { |value| config.instance_ports = value }],
110
+ ['-r', '--relative-root RAILS_RELATIVE_URL_ROOT',
111
+ lambda { |value| config.relative_root = value }],
112
+ ]
113
+ end
114
+
115
+ def generator_options
116
+ [
117
+ ['-g', '--generator GENERATOR',
118
+ %q{Generator to use to output virtualhost configuration file},
119
+ lambda { |value| config.generator = value }],
120
+ ['-o', '--generator-options GENERATOR_OPTIONS',
121
+ %q{Generator options as comma-separated list of key=value},
122
+ lambda { |value| config.generator_options = value }],
123
+ ].freeze
124
+ end
125
+
126
+ # Provide standard exception handling for the given block.
127
+ def standard_exception_handling
128
+ begin
129
+ yield
130
+ rescue SystemExit => ex
131
+ # Exit silently with current status
132
+ raise
133
+ rescue OptionParser::InvalidOption => ex
134
+ $stderr.puts ex.message
135
+ exit(false)
136
+ rescue Exception => ex
137
+ # Exit with error message
138
+ display_error_message(ex)
139
+ exit(false)
140
+ end
141
+ end
142
+
143
+ # Display the error message that caused the exception.
144
+ def display_error_message(ex)
145
+ $stderr.puts "#{@name} aborted!"
146
+ $stderr.puts ex.message
147
+ $stderr.puts ex.backtrace.join("\n")
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,66 @@
1
+ require 'ostruct'
2
+ require 'erb'
3
+
4
+ module VhostGenerator
5
+
6
+ # Nginx VhostGenerator
7
+ #
8
+ class NginxGenerator
9
+ attr_reader :cfg, :options
10
+ def initialize(cfg, options={})
11
+ @cfg = cfg
12
+ @options = OpenStruct.new(default_options.merge(options)).freeze
13
+ end
14
+
15
+ def render
16
+ template.result(binding)
17
+ end
18
+
19
+ protected
20
+
21
+ def default_options
22
+ Hash[ 'client_max_body_size' => '4G', 'keepalive_timeout' => '10',
23
+ 'assets_expire_in' => '60d', 'upstream' => 'appservers' ].freeze
24
+ end
25
+
26
+ private
27
+
28
+ def template
29
+ @template ||= ERB.new <<EOF
30
+ #### FILE GENERATED BY `<%= String(cfg.cmdline) %>`, EDIT AT YOUR OWN RISK ####
31
+
32
+ upstream <%= options.upstream %> {
33
+ <% cfg.instance_ports.each do |p| %> server localhost:<%= p %> fail_timeout=0;
34
+ <% end %>}
35
+
36
+ server {
37
+ <% cfg.server_ports.each do |p| %>listen <%= p %>;
38
+ <% end %>
39
+ <% unless cfg.server_names.empty? %>server_name <%=
40
+ cfg.server_names.join(', ') %>;<% end %>
41
+
42
+ root <%= cfg.static_folder %>;
43
+
44
+ try_files $uri/index.html $uri @upstream;
45
+ location @upstream {
46
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
47
+ proxy_set_header X-Forwarded-Proto $scheme;
48
+ proxy_set_header Host $http_host;
49
+ proxy_redirect off;
50
+ proxy_pass http://<%= options.upstream %>;
51
+ }
52
+
53
+ location <%= cfg.relative_root %>assets {
54
+ gzip_static on; # to serve pre-gzipped version
55
+ expires <%= options.assets_expire_in %>;
56
+ add_header Cache-Control public;
57
+ }
58
+
59
+ error_page 500 502 503 504 /500.html;
60
+ client_max_body_size <%= options.client_max_body_size %>;
61
+ keepalive_timeout <%= options.keepalive_timeout %>;
62
+ }
63
+ EOF
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,3 @@
1
+ module VhostGenerator
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,108 @@
1
+ require 'vhost_generator/nginx_generator'
2
+ require 'shellwords'
3
+
4
+ module VhostGenerator
5
+
6
+ # Represents a Shell command line (to display in vhost comments)
7
+ # TODO: make it a real class with a more narrow interface than Array
8
+ class ShellCmdLine < Array
9
+ def to_str
10
+ parts = self.collect { |cmd| Shellwords.join(cmd) }
11
+ if parts.length > 1
12
+ "(" + parts.join(' && ') + ")"
13
+ else
14
+ parts.join
15
+ end
16
+ end
17
+ end
18
+
19
+ ###########################################################################
20
+ # VhostConfiguration stores all the configuration values (to read from)
21
+ # +env+ or +cmdline+ needed to render the configuration template.
22
+ #
23
+ class VhostConfiguration
24
+ attr_reader :static_folder, :server_ports, :server_names,
25
+ :instance_ports, :relative_root, :cmdline,
26
+ :generator, :generator_options
27
+
28
+ def initialize(static_folder='public', server_ports='80',
29
+ server_names='localhost', instance_ports='', relative_root='/',
30
+ cmdlinebuilder=ShellCmdLine, generator='nginx', generator_options='')
31
+ self.static_folder = static_folder
32
+ self.server_ports = server_ports
33
+ self.server_names = server_names
34
+ self.instance_ports = instance_ports
35
+ self.relative_root = relative_root
36
+ self.cmdline = cmdlinebuilder.new
37
+ self.generator = generator
38
+ self.generator_options = generator_options
39
+ end
40
+
41
+ def static_folder=(folder)
42
+ @static_folder = File.expand_path(folder)
43
+ end
44
+
45
+ def server_ports=(ports)
46
+ @server_ports = parse_integer_list(ports)
47
+ end
48
+
49
+ def server_names=(names)
50
+ @server_names = parse_word_list(names)
51
+ end
52
+
53
+ def instance_ports=(ports)
54
+ @instance_ports = parse_integer_list(ports)
55
+ end
56
+
57
+ def relative_root=(root)
58
+ @relative_root = (String(root) + '/').gsub(%r{/+}, '/')
59
+ end
60
+
61
+ def generator=(name)
62
+ @generator = generator_for(name)
63
+ end
64
+
65
+ def generator_options=(options)
66
+ @generator_options = parse_option_list(options)
67
+ end
68
+
69
+ def configure!(parser)
70
+ parser.parse(self)
71
+ end
72
+
73
+ def output
74
+ self.generator.new(self, self.generator_options).render
75
+ end
76
+
77
+ protected
78
+
79
+ attr_writer :cmdline
80
+
81
+ def generator_for(name)
82
+ raise ArgumentError, "unsupported generator: %s, try any of %s." % [
83
+ name.inspect, registry.keys.inspect
84
+ ] unless registry.has_key?(name)
85
+ registry[name]
86
+ end
87
+
88
+ private
89
+
90
+ attr_writer :registry
91
+ def registry
92
+ # XXX use a real registry to reduce coupling
93
+ @registry ||= {'nginx' => VhostGenerator::NginxGenerator}
94
+ end
95
+
96
+ def parse_word_list(s)
97
+ String(s).split(/[,\s]+/)
98
+ end
99
+
100
+ def parse_option_list(s)
101
+ Hash[ parse_word_list(s).map { |i| i.split('=', 2) } ]
102
+ end
103
+
104
+ def parse_integer_list(s)
105
+ parse_word_list(s).map { |i| Integer(i) }
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,18 @@
1
+ require 'vhost_generator/application'
2
+
3
+ module VhostGenerator
4
+
5
+ # VhostGenerator module singleton methods.
6
+ #
7
+ class << self
8
+ # Current VhostGenerator Application
9
+ def application
10
+ @application ||= VhostGenerator::Application.new
11
+ end
12
+
13
+ # Set the current VhostGenerator application object.
14
+ def application=(app)
15
+ @application = app
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,6 @@
1
+ require "vhost_generator/version"
2
+ require "vhost_generator/vhost_generator_module"
3
+
4
+ module VhostGenerator
5
+ # Your code goes here...
6
+ end
@@ -0,0 +1,29 @@
1
+ require 'vhost_generator/application'
2
+ require 'ostruct'
3
+
4
+ describe VhostGenerator::Application do
5
+ describe "Env" do
6
+ let(:config) { OpenStruct.new }
7
+ before { subject.config = config }
8
+
9
+ options = Hash[
10
+ 'static_folder' => 'STATIC_FOLDER',
11
+ 'server_ports' => 'SERVER_PORTS',
12
+ 'server_names' => 'SERVER_NAMES',
13
+ 'instance_ports' => 'INSTANCE_PORTS',
14
+ 'relative_root' => 'RAILS_RELATIVE_URL_ROOT',
15
+ 'generator' => 'GENERATOR',
16
+ 'generator_options' => 'GENERATOR_OPTIONS',
17
+ ]
18
+
19
+ options.each_pair do |name, var|
20
+ describe "#{name} option" do
21
+ it "is set by the #{var} variable" do
22
+ expect {
23
+ subject.handle_env(var => 'value')
24
+ }.to change(config, name).to('value')
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ require 'vhost_generator/application'
2
+ require 'ostruct'
3
+
4
+ describe VhostGenerator::Application do
5
+ describe "Options" do
6
+ let(:config) { OpenStruct.new }
7
+ before { subject.config = config }
8
+
9
+ options = Hash[
10
+ 'static_folder' => %w(-f --static-folder),
11
+ 'server_ports' => %w(-l --listen),
12
+ 'server_names' => %w(-s --server-name),
13
+ 'instance_ports' => %w(-p --instance-port),
14
+ 'relative_root' => %w(-r --relative-root),
15
+ 'generator' => %w(-g --generator),
16
+ 'generator_options' => %w(-o --generator-options)
17
+ ]
18
+
19
+ options.each_pair do |name, flags|
20
+ describe "#{name} option" do
21
+ flags.each do |flag|
22
+ it "is set by the #{flag} flag" do
23
+ expect {
24
+ subject.handle_options([flag, 'value'])
25
+ }.to change(config, name).to('value')
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,61 @@
1
+ require 'vhost_generator/nginx_generator'
2
+
3
+ describe VhostGenerator::NginxGenerator do
4
+ let(:config) { double('vhost config').as_null_object }
5
+ subject do
6
+ described_class.new(config, 'upstream' => 'myupstream')
7
+ end
8
+
9
+ describe "#render" do
10
+ let(:output) { subject.render }
11
+
12
+ it "includes the cmdline in a comment" do
13
+ config.stub(:cmdline).and_return('CMDLINE')
14
+ expect(output).to match(/^#### FILE GENERATED BY .*CMDLINE/)
15
+ end
16
+
17
+ it "declares the named upstream" do
18
+ expect(output).to include('upstream myupstream {')
19
+ end
20
+
21
+ it "references the named upstream" do
22
+ expect(output).to include('http://myupstream;')
23
+ end
24
+
25
+ it "declares all the requested upstream servers" do
26
+ config.stub(:instance_ports).and_return([1337, 1338])
27
+ expect(output).to include('server localhost:1337 fail_timeout=0;')
28
+ expect(output).to include('server localhost:1338 fail_timeout=0;')
29
+ end
30
+
31
+ it "listens to the requested server ports" do
32
+ config.stub(:server_ports).and_return([12345, 12346])
33
+ expect(output).to include('listen 12345;')
34
+ expect(output).to include('listen 12346;')
35
+ end
36
+
37
+ it "declares the server names it responds to" do
38
+ config.stub(:server_names).and_return(%w(host1 host2))
39
+ expect(output).to include('server_name host1, host2;')
40
+ end
41
+
42
+ it "declares the requested document root" do
43
+ config.stub(:static_folder).and_return('STATIC-FOLDER')
44
+ expect(output).to include('root STATIC-FOLDER;')
45
+ end
46
+
47
+ it "forwards X-Forwarded-For header" do
48
+ expect(output).to \
49
+ include('proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;')
50
+ end
51
+
52
+ it "forwards X-Forwarded-Proto header" do
53
+ expect(output).to include('proxy_set_header X-Forwarded-Proto $scheme;')
54
+ end
55
+
56
+ it "respects custom relative_roots" do
57
+ config.stub(:relative_root).and_return('RELATIVE_ROOT')
58
+ expect(output).to include('location RELATIVE_ROOTassets {')
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,127 @@
1
+ require 'vhost_generator/vhost_configuration'
2
+
3
+ describe VhostGenerator::VhostConfiguration do
4
+ describe "#static_folder" do
5
+ it "is 'public/' by default" do
6
+ expect(subject.static_folder).to eql(File.expand_path('public'))
7
+ end
8
+
9
+ it "expanded" do
10
+ expect {
11
+ subject.static_folder = 'html'
12
+ }.to change(subject, :static_folder).to File.expand_path('html')
13
+ end
14
+ end
15
+
16
+ describe "#server_ports" do
17
+ it "is 80 by default" do
18
+ expect(subject.server_ports).to eql([80])
19
+ end
20
+
21
+ it "parses to Array of Integer" do
22
+ expect {
23
+ subject.server_ports = '80,81'
24
+ }.to change(subject, :server_ports).to [80, 81]
25
+ end
26
+
27
+ it "complains when trying to set invalid values" do
28
+ expect {
29
+ subject.server_ports = '80a'
30
+ }.to raise_error(ArgumentError, /invalid value for Integer/)
31
+ end
32
+ end
33
+
34
+ describe "#server_names" do
35
+ it "is 'localhost' by default" do
36
+ expect(subject.server_names).to eql(%w(localhost))
37
+ end
38
+
39
+ it "parses to Array of String" do
40
+ expect {
41
+ subject.server_names = 'localhost , test.host'
42
+ }.to change(subject, :server_names).to %w(localhost test.host)
43
+ end
44
+ end
45
+
46
+ describe "#instance_ports" do
47
+ it "is empty by default" do
48
+ expect(subject.instance_ports).to be_empty
49
+ end
50
+
51
+ it "parses to Array of Integer" do
52
+ expect {
53
+ subject.instance_ports = '5000,5001'
54
+ }.to change(subject, :instance_ports).to [5000, 5001]
55
+ end
56
+
57
+ it "complains when trying to set invalid values" do
58
+ expect {
59
+ subject.instance_ports = '5000a'
60
+ }.to raise_error(ArgumentError, /invalid value for Integer/)
61
+ end
62
+ end
63
+
64
+ describe "#relative_root" do
65
+ it "is '/' by default" do
66
+ expect(subject.relative_root).to eql('/')
67
+ end
68
+
69
+ it "appends '/' at end" do
70
+ expect {
71
+ subject.relative_root = '/myapp'
72
+ }.to change(subject, :relative_root).to '/myapp/'
73
+ end
74
+
75
+ it "strips redundant slashes" do
76
+ expect {
77
+ subject.relative_root = '/path//to///myapp////'
78
+ }.to change(subject, :relative_root).to '/path/to/myapp/'
79
+ end
80
+ end
81
+
82
+ describe "#cmdline" do
83
+ it "is empty by default" do
84
+ subject.cmdline.should be_empty
85
+ end
86
+
87
+ it "can be append to using the shovel operator" do
88
+ subject.cmdline.should respond_to(:<<)
89
+ end
90
+
91
+ it "can be coerced into string" do
92
+ subject.cmdline.should respond_to(:to_str)
93
+ end
94
+ end
95
+
96
+ describe "#generator" do
97
+ it "is present by default" do
98
+ expect(subject.generator).to be
99
+ end
100
+
101
+ it "is resolved into a generator plugin" do
102
+ generator = double('generator')
103
+ subject.send(:registry).merge!('test' => generator)
104
+ expect {
105
+ subject.generator = 'test'
106
+ }.to change(subject, :generator).to generator
107
+ end
108
+
109
+ it "complains when trying to set invalid values" do
110
+ expect {
111
+ subject.generator = 'xyzzy'
112
+ }.to raise_error(ArgumentError, /unsupported generator/)
113
+ end
114
+ end
115
+
116
+ describe "#generator_options" do
117
+ it "is empty by default" do
118
+ expect(subject.generator_options).to be_empty
119
+ end
120
+
121
+ it "parses to Hash[key=value]" do
122
+ expect {
123
+ subject.generator_options = 'k=a=b , l=j'
124
+ }.to change(subject, :generator_options).to Hash['k' => 'a=b', 'l' => 'j']
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'vhost_generator/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "vhost_generator"
8
+ gem.version = VhostGenerator::VERSION
9
+ gem.authors = ["Julien Pervillé"]
10
+ gem.email = ["julien.perville@mingalar.fr"]
11
+ gem.description = ['vhost_generator outputs a general-purpose VirtualHost',
12
+ 'configuration file to run your web application behind',
13
+ 'an nginx or apache frontend'].join(' ')
14
+ gem.summary = ['vhost_generator outputs nginx or apache VirtualHost',
15
+ 'configurations to run your web application'].join(' ')
16
+ gem.homepage = "https://github.com/mingalar/vhost_generator"
17
+
18
+ gem.files = `git ls-files`.split($/)
19
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
20
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
21
+ gem.require_paths = ["lib"]
22
+
23
+ gem.add_runtime_dependency('dotenv', '~> 0.2.0')
24
+ gem.add_development_dependency('rake', '~> 0.9.2.2')
25
+ gem.add_development_dependency('cucumber', '~> 1.2.1')
26
+ gem.add_development_dependency('aruba', '~> 0.4.11')
27
+ gem.add_development_dependency('rspec', '~> 2.11.0')
28
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vhost_generator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Julien Pervillé
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: dotenv
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.2.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.2.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.9.2.2
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.9.2.2
46
+ - !ruby/object:Gem::Dependency
47
+ name: cucumber
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.2.1
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.2.1
62
+ - !ruby/object:Gem::Dependency
63
+ name: aruba
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.4.11
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 0.4.11
78
+ - !ruby/object:Gem::Dependency
79
+ name: rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 2.11.0
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 2.11.0
94
+ description: vhost_generator outputs a general-purpose VirtualHost configuration file
95
+ to run your web application behind an nginx or apache frontend
96
+ email:
97
+ - julien.perville@mingalar.fr
98
+ executables:
99
+ - vhost-generator
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - .gitignore
104
+ - Gemfile
105
+ - LICENSE.txt
106
+ - README.md
107
+ - Rakefile
108
+ - bin/vhost-generator
109
+ - features/help.feature
110
+ - features/output-nginx.feature
111
+ - features/step_definitions/dev_steps.rb
112
+ - features/support/env.rb
113
+ - features/version.feature
114
+ - lib/vhost_generator.rb
115
+ - lib/vhost_generator/application.rb
116
+ - lib/vhost_generator/nginx_generator.rb
117
+ - lib/vhost_generator/version.rb
118
+ - lib/vhost_generator/vhost_configuration.rb
119
+ - lib/vhost_generator/vhost_generator_module.rb
120
+ - spec/application_env_spec.rb
121
+ - spec/application_options_spec.rb
122
+ - spec/nginx_generator_spec.rb
123
+ - spec/vhost_configuration_spec.rb
124
+ - vhost_generator.gemspec
125
+ homepage: https://github.com/mingalar/vhost_generator
126
+ licenses: []
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ! '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ segments:
138
+ - 0
139
+ hash: -198369393
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ! '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ segments:
147
+ - 0
148
+ hash: -198369393
149
+ requirements: []
150
+ rubyforge_project:
151
+ rubygems_version: 1.8.23
152
+ signing_key:
153
+ specification_version: 3
154
+ summary: vhost_generator outputs nginx or apache VirtualHost configurations to run
155
+ your web application
156
+ test_files:
157
+ - features/help.feature
158
+ - features/output-nginx.feature
159
+ - features/step_definitions/dev_steps.rb
160
+ - features/support/env.rb
161
+ - features/version.feature
162
+ - spec/application_env_spec.rb
163
+ - spec/application_options_spec.rb
164
+ - spec/nginx_generator_spec.rb
165
+ - spec/vhost_configuration_spec.rb