vhost_generator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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