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