vhost_generator 0.2.0 → 0.2.1
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/bin/foreman-export-vhost +260 -0
- data/features/foreman-export-vhost.feature +62 -0
- data/features/version.feature +1 -1
- data/lib/vhost_generator/version.rb +1 -1
- metadata +8 -4
@@ -0,0 +1,260 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
=begin
|
4
|
+
Wraps "foreman export" and "vhost-generator" in a single command.
|
5
|
+
TODO: rewrite as a real application, not just a glorified shell script.
|
6
|
+
=end
|
7
|
+
|
8
|
+
require 'optparse'
|
9
|
+
require 'ostruct'
|
10
|
+
require 'shellwords'
|
11
|
+
|
12
|
+
class ForemanExportApplication
|
13
|
+
def initialize(output_stream)
|
14
|
+
@output_stream = output_stream
|
15
|
+
end
|
16
|
+
|
17
|
+
def run(argv)
|
18
|
+
standard_exception_handling do
|
19
|
+
handle_arguments!(argv)
|
20
|
+
apply_defaults_and_sanitize_config
|
21
|
+
config.instance_ports = InstancePortCalculator.new(config).apply
|
22
|
+
commands = String(ShellBuilder.new(config, argv).apply)
|
23
|
+
if config.dry_run
|
24
|
+
puts commands
|
25
|
+
puts "# Now, try to run this script again without the --dry-run switch!"
|
26
|
+
else
|
27
|
+
exec commands
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def config
|
35
|
+
@config ||= OpenStruct.new
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_arguments!(argv)
|
39
|
+
OptionParser.new do |opts|
|
40
|
+
opts.banner = "Usage: foreman-export-vhost FORMAT LOCATION"
|
41
|
+
|
42
|
+
opts.separator ""
|
43
|
+
opts.separator "Foreman options:"
|
44
|
+
foreman_options.each { |args| opts.on(*args) }
|
45
|
+
|
46
|
+
opts.separator ""
|
47
|
+
opts.separator "Vhost-generator options:"
|
48
|
+
generator_options.each { |args| opts.on(*args) }
|
49
|
+
|
50
|
+
opts.separator ""
|
51
|
+
|
52
|
+
opts.on_tail("-h", "--help", "-H", "Display this help message.") do
|
53
|
+
puts opts
|
54
|
+
exit(true)
|
55
|
+
end
|
56
|
+
end.parse!(argv)
|
57
|
+
end
|
58
|
+
|
59
|
+
def foreman_options
|
60
|
+
[
|
61
|
+
['-a', '--app=APP', lambda { |value| config.app = value }],
|
62
|
+
['-l', '--log=LOG', lambda { |value| config.log = value }],
|
63
|
+
['-e', '--env=ENV',
|
64
|
+
'# Specify an environment file to load, defaults to .env',
|
65
|
+
lambda { |value| config.env = value }],
|
66
|
+
['-p', '--port=N',
|
67
|
+
'# Default: 5000',
|
68
|
+
lambda { |value| config.port = Integer(value) }],
|
69
|
+
['-u', '--user=USER', lambda { |value| config.user = value }],
|
70
|
+
['-t', '--template=TEMPLATE', lambda { |value| config.template = value }],
|
71
|
+
['-c', '--concurrency=alpha=5,bar=3',
|
72
|
+
lambda { |value| config.concurrency = value }],
|
73
|
+
['-f', '--procfile=PROCFILE',
|
74
|
+
'# Default: Procfile',
|
75
|
+
lambda { |value| config.procfile = value }],
|
76
|
+
['-d', '--root=ROOT',
|
77
|
+
'# Default: Procfile directory',
|
78
|
+
lambda { |value| config.root = value }],
|
79
|
+
].freeze
|
80
|
+
end
|
81
|
+
|
82
|
+
def generator_options
|
83
|
+
[
|
84
|
+
['-F', '--static-folder=FOLDER',
|
85
|
+
'# Default: "public" folder in Procfile directory',
|
86
|
+
lambda { |value| config.static_folder = value }],
|
87
|
+
['-L', '--server-ports=PORTS',
|
88
|
+
'# Default: 80',
|
89
|
+
lambda { |value| config.server_ports = value }],
|
90
|
+
['-S', '--server-names=NAMES',
|
91
|
+
'# Default: localhost',
|
92
|
+
lambda { |value| config.server_names = value }],
|
93
|
+
['-K', '--foreman-process-type=TYPE',
|
94
|
+
'# Default: web (must have entry of that name in Procfile)',
|
95
|
+
lambda { |value| config.process_type = value }],
|
96
|
+
['-G', '--generator=GENERATOR',
|
97
|
+
'# Default: nginx (only supported for now)',
|
98
|
+
lambda { |value| config.generator = value }],
|
99
|
+
['-N', '--dry-run', lambda { |value| config.dry_run = true }],
|
100
|
+
['-R', '--stop-start-service',
|
101
|
+
'# Starts and stops APP service (may not work everywhere)',
|
102
|
+
lambda { |value| config.service = true }],
|
103
|
+
].freeze
|
104
|
+
end
|
105
|
+
|
106
|
+
def apply_defaults_and_sanitize_config
|
107
|
+
config.app ||= 'myapp'
|
108
|
+
config.port ||= 5000
|
109
|
+
config.procfile ||= 'Procfile'
|
110
|
+
config.procfile = File.expand_path(config.procfile)
|
111
|
+
config.root ||= File.dirname(config.procfile)
|
112
|
+
config.root = File.expand_path(config.root)
|
113
|
+
config.static_folder ||= File.join(config.root, 'public')
|
114
|
+
config.static_folder = File.expand_path(config.static_folder)
|
115
|
+
config.generator ||= 'nginx'
|
116
|
+
config.process_type ||= 'web'
|
117
|
+
end
|
118
|
+
|
119
|
+
class InstancePortCalculator
|
120
|
+
attr_reader :config
|
121
|
+
|
122
|
+
def initialize(config)
|
123
|
+
@config = config
|
124
|
+
end
|
125
|
+
|
126
|
+
def apply
|
127
|
+
c = process_concurrency(config.concurrency, config.process_type)
|
128
|
+
o = process_offset(config.procfile, config.process_type)
|
129
|
+
raise ArgumentError, "Can't find process type %s in %s, sorry." % [
|
130
|
+
config.process_type.inspect, config.procfile.inspect
|
131
|
+
] unless o
|
132
|
+
base_port = config.port + 100 * o
|
133
|
+
(base_port...base_port + c).to_a.join(',')
|
134
|
+
end
|
135
|
+
|
136
|
+
def process_concurrency(concurrency, process_type)
|
137
|
+
if concurrency && process_type
|
138
|
+
per_type = Hash[concurrency.split(',').map { |v| v.split('=', 2) }]
|
139
|
+
Integer(per_type[process_type] || 1)
|
140
|
+
else
|
141
|
+
1
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def process_offset(procfile, process_type)
|
146
|
+
process_type_re = %r(#{Regexp.escape(process_type)}:)
|
147
|
+
File.read(procfile).lines.each_with_index do |l,i|
|
148
|
+
return i if l =~ process_type_re
|
149
|
+
end
|
150
|
+
nil # not found
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class ShellBuilder
|
155
|
+
def initialize(config, argv, commands=Array.new)
|
156
|
+
@config = config
|
157
|
+
@argv = argv
|
158
|
+
@commands = commands
|
159
|
+
end
|
160
|
+
|
161
|
+
def to_str
|
162
|
+
commands.collect { |c| c.join(' ') }.join($/)
|
163
|
+
end
|
164
|
+
|
165
|
+
def apply
|
166
|
+
commands << %w(bundle exec foreman run vhost-generator) +
|
167
|
+
escape(generator_flags) +
|
168
|
+
%w(| sudo tee) + escape(vhost_config)
|
169
|
+
commands << %w(sudo service) + escape(service) + %w(reload)
|
170
|
+
commands << %w(sudo service) + escape(app) +
|
171
|
+
%w(stop >/dev/null 2>&1 || true) if config.service
|
172
|
+
commands << %w(sudo rm -rf) +
|
173
|
+
Array(Shellwords.escape("#{target}/#{app}") + "-*.conf")
|
174
|
+
commands << %w(sudo bundle exec foreman export) +
|
175
|
+
escape(argv + foreman_flags)
|
176
|
+
commands << %w(sudo service) + escape(app) + %w(start)
|
177
|
+
commands << ['echo',
|
178
|
+
'"Finished, now open your browser and see if all works."']
|
179
|
+
self
|
180
|
+
end
|
181
|
+
|
182
|
+
protected
|
183
|
+
|
184
|
+
attr_reader :config, :argv, :commands
|
185
|
+
|
186
|
+
def generator_flags(flags=Array.new)
|
187
|
+
flags << '-f' << config.static_folder if config.static_folder
|
188
|
+
flags << '-l' << config.server_ports if config.server_ports
|
189
|
+
flags << '-s' << config.server_names if config.server_names
|
190
|
+
flags << '-p' << config.instance_ports if config.instance_ports
|
191
|
+
flags << '-g' << config.generator if config.generator
|
192
|
+
flags << '-o' << "upstream=#{app}" if config.generator == 'nginx' && app
|
193
|
+
end
|
194
|
+
|
195
|
+
def foreman_flags(flags=Array.new)
|
196
|
+
flags << '-a' << config.app if config.app
|
197
|
+
flags << '-l' << config.log if config.log
|
198
|
+
flags << '-e' << config.env if config.env
|
199
|
+
flags << '-p' << String(config.port) if config.port
|
200
|
+
flags << '-u' << config.user if config.user
|
201
|
+
flags << '-t' << config.template if config.template
|
202
|
+
flags << '-c' << config.concurrency if config.concurrency
|
203
|
+
flags << '-f' << config.procfile if config.procfile
|
204
|
+
flags << '-d' << config.root if config.root
|
205
|
+
end
|
206
|
+
|
207
|
+
def app
|
208
|
+
config.app
|
209
|
+
end
|
210
|
+
|
211
|
+
def service
|
212
|
+
config.generator # nginx
|
213
|
+
end
|
214
|
+
|
215
|
+
def target
|
216
|
+
argv[1] # eg. /etc/init
|
217
|
+
end
|
218
|
+
|
219
|
+
def vhost_config
|
220
|
+
"/etc/#{service}/sites-enabled/rails-#{app}.conf"
|
221
|
+
end
|
222
|
+
|
223
|
+
def escape(args)
|
224
|
+
Array(args).map { |c| Shellwords.shellescape(c) }
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
private
|
229
|
+
|
230
|
+
def puts(*args)
|
231
|
+
@output_stream.puts(*args)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Provide standard exception handling for the given block.
|
235
|
+
def standard_exception_handling
|
236
|
+
begin
|
237
|
+
yield
|
238
|
+
rescue SystemExit => ex
|
239
|
+
# Exit silently with current status
|
240
|
+
raise
|
241
|
+
rescue OptionParser::InvalidOption => ex
|
242
|
+
$stderr.puts ex.message
|
243
|
+
exit(false)
|
244
|
+
rescue Exception => ex
|
245
|
+
# Exit with error message
|
246
|
+
display_error_message(ex)
|
247
|
+
exit(false)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# Display the error message that caused the exception.
|
252
|
+
def display_error_message(ex)
|
253
|
+
$stderr.puts "#{@name} aborted!"
|
254
|
+
$stderr.puts ex.message
|
255
|
+
$stderr.puts ex.backtrace.join("\n")
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
ForemanExportApplication.new($stdout).run(ARGV)
|
260
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
Feature: Wrap "foreman export"
|
2
|
+
|
3
|
+
In order to generate and install a foreman web application to a virtualhost
|
4
|
+
As a user of the library
|
5
|
+
I want to run "foreman export" and "vhost-generator" together with DRY arguments.
|
6
|
+
|
7
|
+
Scenario: display help
|
8
|
+
When I run `bundle exec foreman-export-vhost --help`
|
9
|
+
Then it should pass with:
|
10
|
+
"""
|
11
|
+
Usage: foreman-export-vhost FORMAT LOCATION
|
12
|
+
|
13
|
+
Foreman options:
|
14
|
+
-a, --app=APP
|
15
|
+
-l, --log=LOG
|
16
|
+
-e, --env=ENV # Specify an environment file to load, defaults to .env
|
17
|
+
-p, --port=N # Default: 5000
|
18
|
+
-u, --user=USER
|
19
|
+
-t, --template=TEMPLATE
|
20
|
+
-c, --concurrency=alpha=5,bar=3
|
21
|
+
-f, --procfile=PROCFILE # Default: Procfile
|
22
|
+
-d, --root=ROOT # Default: Procfile directory
|
23
|
+
|
24
|
+
Vhost-generator options:
|
25
|
+
-F, --static-folder=FOLDER # Default: "public" folder in Procfile directory
|
26
|
+
-L, --server-ports=PORTS # Default: 80
|
27
|
+
-S, --server-names=NAMES # Default: localhost
|
28
|
+
-K, --foreman-process-type=TYPE # Default: web (must have entry of that name in Procfile)
|
29
|
+
-G, --generator=GENERATOR # Default: nginx (only supported for now)
|
30
|
+
-N, --dry-run
|
31
|
+
-R, --stop-start-service # Starts and stops APP service (may not work everywhere)
|
32
|
+
|
33
|
+
-h, -H, --help Display this help message.
|
34
|
+
"""
|
35
|
+
|
36
|
+
Scenario: display source that will be executed
|
37
|
+
Given a file named "Procfile" with:
|
38
|
+
"""
|
39
|
+
clock: ....
|
40
|
+
web: bundle exec webserver -p $PORT
|
41
|
+
"""
|
42
|
+
When I run `bundle exec foreman-export-vhost upstart /etc/init -a MYAPP -u MYUSER -p 6000 -c clock=1,web=2 -L 80,81 -S localhost,myapp.com -K web -G nginx -N -R`
|
43
|
+
Then the output should match:
|
44
|
+
"""
|
45
|
+
bundle exec foreman run vhost-generator -f /.*/public -l 80,81 -s localhost,myapp.com -p 6100,6101 -g nginx -o upstream=MYAPP | sudo tee /etc/nginx/sites-enabled/rails-MYAPP.conf
|
46
|
+
"""
|
47
|
+
And the output should contain:
|
48
|
+
"""
|
49
|
+
sudo service nginx reload
|
50
|
+
sudo service MYAPP stop >/dev/null 2>&1 || true
|
51
|
+
sudo rm -rf /etc/init/MYAPP-*.conf
|
52
|
+
"""
|
53
|
+
And the output should match:
|
54
|
+
"""
|
55
|
+
sudo bundle exec foreman export upstart /etc/init -a MYAPP -p 6000 -u MYUSER -c clock\\=1,web\\=2 -f /.*/Procfile -d /.*$
|
56
|
+
"""
|
57
|
+
And the output should contain:
|
58
|
+
"""
|
59
|
+
sudo service MYAPP start
|
60
|
+
echo "Finished, now open your browser and see if all works."
|
61
|
+
# Now, try to run this script again without the --dry-run switch!
|
62
|
+
"""
|
data/features/version.feature
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vhost_generator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
12
|
+
date: 2012-10-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -80,6 +80,7 @@ description: vhost_generator outputs a general-purpose VirtualHost configuration
|
|
80
80
|
email:
|
81
81
|
- julien.perville@mingalar.fr
|
82
82
|
executables:
|
83
|
+
- foreman-export-vhost
|
83
84
|
- vhost-generator
|
84
85
|
extensions: []
|
85
86
|
extra_rdoc_files: []
|
@@ -89,7 +90,9 @@ files:
|
|
89
90
|
- LICENSE.txt
|
90
91
|
- README.md
|
91
92
|
- Rakefile
|
93
|
+
- bin/foreman-export-vhost
|
92
94
|
- bin/vhost-generator
|
95
|
+
- features/foreman-export-vhost.feature
|
93
96
|
- features/help.feature
|
94
97
|
- features/output-nginx.feature
|
95
98
|
- features/step_definitions/dev_steps.rb
|
@@ -123,7 +126,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
123
126
|
version: '0'
|
124
127
|
segments:
|
125
128
|
- 0
|
126
|
-
hash: -
|
129
|
+
hash: -251905965
|
127
130
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
131
|
none: false
|
129
132
|
requirements:
|
@@ -132,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
135
|
version: '0'
|
133
136
|
segments:
|
134
137
|
- 0
|
135
|
-
hash: -
|
138
|
+
hash: -251905965
|
136
139
|
requirements: []
|
137
140
|
rubyforge_project:
|
138
141
|
rubygems_version: 1.8.23
|
@@ -141,6 +144,7 @@ specification_version: 3
|
|
141
144
|
summary: vhost_generator outputs nginx or apache VirtualHost configurations to run
|
142
145
|
your web application
|
143
146
|
test_files:
|
147
|
+
- features/foreman-export-vhost.feature
|
144
148
|
- features/help.feature
|
145
149
|
- features/output-nginx.feature
|
146
150
|
- features/step_definitions/dev_steps.rb
|