fly.io-rails 0.1.7 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +19 -0
- data/lib/fly.io-rails/actions.rb +116 -71
- data/lib/fly.io-rails/scanner.rb +3 -1
- data/lib/fly.io-rails/utils.rb +2 -0
- data/lib/fly.io-rails/version.rb +1 -1
- data/lib/generators/fly/app_generator.rb +3 -0
- data/lib/generators/fly/terraform_generator.rb +3 -0
- data/lib/generators/templates/Dockerfile.erb +46 -14
- data/lib/generators/templates/fly.rake.erb +35 -6
- data/lib/generators/templates/fly.toml.erb +7 -0
- data/lib/generators/templates/hook_detached_process.erb +1 -5
- data/lib/generators/templates/main.tf.erb +7 -2
- data/lib/generators/templates/nginx.conf.erb +28 -10
- data/lib/tasks/fly.rake +36 -0
- data/lib/tasks/mock.rake +17 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5607b08f46147018d9b5915b484ccb3d20f76a662aee1b381d5576cfc9e6b5f9
|
4
|
+
data.tar.gz: 0e7ae042c84afd01f3fa6ab90dab72a4720b7f5e816d9859c59007ebad06635b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1849e0cca1382bc6df929ab82a777bbe9586be9bf6ad6b26e2fe5f8942fc43ef8661c8ea46cddafb53eb6997f7b1ff4611b2f4ecbd896a2fe5160aaf77ad6077
|
7
|
+
data.tar.gz: 13ceffa34870da0b65bbb16d488d730e12e9592f6862cb50d594f9bf034dda62f8251ea6878634b8a682b1c060dbbefb63f3484bd9411c3cba4fe462e0b54f2a
|
data/README.md
CHANGED
@@ -10,6 +10,25 @@ For usage instructions, see the following guides:
|
|
10
10
|
* [Lite FS](https://fly.io/docs/rails/advanced-guides/litefs/)
|
11
11
|
* [Terraform](https://fly.io/docs/rails/advanced-guides/terraform/)
|
12
12
|
|
13
|
+
## Generator options
|
14
|
+
|
15
|
+
* `--name` name of the application. If a name is not provided, one will be generated for you.
|
16
|
+
* `--org` the organization to operate on. Defaults to `personal`.
|
17
|
+
* `--region` region to launch the application in. Accepts multiple values, and can be specified multiple times.
|
18
|
+
* `--nomad` generates a nomad application instead of a machines application.
|
19
|
+
* `--litefs` adds support for replicated sqlite3 databases via [litefs](https://fly.io/blog/introducing-litefs/). Only works on nomad machines currently.
|
20
|
+
* `--passenger` run your Rails application with [nginx](https://www.nginx.com/) and [Phusion Passenger](https://www.phusionpassenger.com/).
|
21
|
+
* `--serverless` configures your application to exit after 5 minutes of inactivity. Machines will automatically restart when next accessed. Only works with passenger currently.
|
22
|
+
|
23
|
+
## Automatically detected features
|
24
|
+
|
25
|
+
* _ruby_: the deployed application will use the same version of ruby and bundler as your development environment.
|
26
|
+
* _node_: if the use of node is detected, node, yarn, and your npm packages will be installed.
|
27
|
+
* _sqlite3_: if the production database is sqlite3 a volume will be allocated and the database will be put there.
|
28
|
+
* _postgres_: if the production database is postgres a postgres machine will be allocated
|
29
|
+
* _redis_: if redis is used for action cable, caching, or sidekiq your redis database will be added to this application. If you don't currently have a redis database, one will be allocated. If redis is used for caching, eviction will be turned on.
|
30
|
+
* _sidekiq_: if sidekiq is used it will be launched along side of your rails application.
|
31
|
+
|
13
32
|
## Key files
|
14
33
|
|
15
34
|
* Entrypoints: [lib/tasks/fly.rake](./lib/tasks/fly.rake), [lib/generators/app_generator.rb](./lib/generators/app_generator.rb), [lib/generators/terraform_generator.rb](.lib/generators/terraform_generator.rb) contain the deploy task, fly:app generator and
|
data/lib/fly.io-rails/actions.rb
CHANGED
@@ -24,6 +24,7 @@ module Fly
|
|
24
24
|
# extract options
|
25
25
|
self.app = app
|
26
26
|
regions = options[:region]&.flatten || []
|
27
|
+
@avahi = options[:avahi]
|
27
28
|
@litefs = options[:litefs]
|
28
29
|
@nomad = options[:nomad]
|
29
30
|
@passenger = options[:passenger]
|
@@ -58,11 +59,43 @@ module Fly
|
|
58
59
|
|
59
60
|
# set additional variables based on application source
|
60
61
|
scan_rails_app
|
62
|
+
@redis = :internal if options[:redis]
|
63
|
+
if File.exist? 'Procfile.fly'
|
64
|
+
@redis = :internal if IO.read('Procfile.fly') =~ /^redis/
|
65
|
+
end
|
66
|
+
|
67
|
+
if options[:anycable] and not @anycable
|
68
|
+
# read and remove original config
|
69
|
+
original_config = YAML.load_file 'config/cable.yml'
|
70
|
+
File.unlink 'config/cable.yml'
|
71
|
+
|
72
|
+
# add and configure anycable-rails
|
73
|
+
say_status :run, 'bundle add anycable-rails'
|
74
|
+
Bundler.with_original_env do
|
75
|
+
system 'bundle add anycable-rails'
|
76
|
+
system 'bin/rails generate anycable:setup --skip-heroku --skip-procfile-dev --skip-jwt --devenv=skip'
|
77
|
+
end
|
78
|
+
|
79
|
+
# insert action_cable_meta_tag
|
80
|
+
insert_into_file 'app/views/layouts/application.html.erb',
|
81
|
+
" <%= action_cable_meta_tag %>\n",
|
82
|
+
after: "<%= csp_meta_tag %>\n"
|
83
|
+
|
84
|
+
# copy production environment to original config
|
85
|
+
anycable_config = YAML.load_file 'config/cable.yml'
|
86
|
+
original_config['production'] = anycable_config['production']
|
87
|
+
File.write 'config/cable.yml', YAML.dump(original_config)
|
88
|
+
|
89
|
+
@anycable = true
|
90
|
+
end
|
61
91
|
|
62
92
|
# determine processes
|
63
93
|
@procs = {web: 'bin/rails server'}
|
64
94
|
@procs[:web] = "nginx -g 'daemon off;'" if @passenger
|
65
95
|
@procs[:worker] = 'bundle exec sidekiq' if @sidekiq
|
96
|
+
@procs[:redis] = 'redis-server /etc/redis/redis.conf' if @redis
|
97
|
+
@procs.merge! 'anycable-rpc': 'bundle exec anycable',
|
98
|
+
'anycable-go': '/usr/local/bin/anycable-go --port=8082' if @anycable
|
66
99
|
end
|
67
100
|
|
68
101
|
def app
|
@@ -143,7 +176,9 @@ module Fly
|
|
143
176
|
end
|
144
177
|
|
145
178
|
def generate_patches
|
146
|
-
if @redis_cable and not
|
179
|
+
if @redis_cable and not @anycable and @redis != :internal and
|
180
|
+
not File.exist? 'config/initializers/action_cable.rb'
|
181
|
+
|
147
182
|
app
|
148
183
|
template 'patches/action_cable.rb', 'config/initializers/action_cable.rb'
|
149
184
|
end
|
@@ -204,7 +239,7 @@ module Fly
|
|
204
239
|
start = Fly::Machines.create_and_start_machine(app, config: config)
|
205
240
|
machine = start[:id]
|
206
241
|
|
207
|
-
if
|
242
|
+
if not machine
|
208
243
|
STDERR.puts 'Error starting release machine'
|
209
244
|
PP.pp start, STDERR
|
210
245
|
exit 1
|
@@ -214,24 +249,27 @@ module Fly
|
|
214
249
|
timeout: 60, state: 'started'
|
215
250
|
|
216
251
|
# wait for release to copmlete
|
217
|
-
status = nil
|
218
252
|
5.times do
|
219
253
|
status = Fly::Machines.wait_for_machine app, machine,
|
220
|
-
timeout: 60, state: 'stopped'
|
221
|
-
|
254
|
+
instance_id: start[:instance_id], timeout: 60, state: 'stopped'
|
255
|
+
break if status[:ok]
|
222
256
|
end
|
223
257
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
258
|
+
if status and status[:ok]
|
259
|
+
event = nil
|
260
|
+
300.times do
|
261
|
+
status = Fly::Machines.get_a_machine app, start[:id]
|
262
|
+
event = status[:events]&.first
|
263
|
+
break if event[:type] == 'exit'
|
264
|
+
sleep 0.2
|
265
|
+
end
|
232
266
|
|
233
|
-
|
234
|
-
|
267
|
+
exit_code = event&.dig(:request, :exit_event, :exit_code)
|
268
|
+
Fly::Machines.delete_machine app, machine if machine
|
269
|
+
return event, exit_code, machine
|
270
|
+
else
|
271
|
+
return status, nil, nil
|
272
|
+
end
|
235
273
|
end
|
236
274
|
|
237
275
|
def launch(app)
|
@@ -263,7 +301,7 @@ module Fly
|
|
263
301
|
end
|
264
302
|
end
|
265
303
|
|
266
|
-
if @redis and not secrets.include? 'REDIS_URL'
|
304
|
+
if @redis and @redis != :internal and not secrets.include? 'REDIS_URL'
|
267
305
|
# Set eviction policy to true if a cache provider, else false.
|
268
306
|
eviction = @redis_cache ? '--enable-eviction' : '--disable-eviction'
|
269
307
|
|
@@ -313,22 +351,30 @@ module Fly
|
|
313
351
|
|
314
352
|
# perform release
|
315
353
|
say_status :fly, release_config[:env]['SERVER_COMMAND']
|
316
|
-
machine = release(app, release_config)
|
317
|
-
Fly::Machines.delete_machine app, machine if machine
|
318
|
-
|
319
|
-
# start proxy, if necessary
|
320
|
-
endpoint = Fly::Machines::fly_api_hostname!
|
354
|
+
event, exit_code, machine = release(app, release_config)
|
321
355
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
system "fly machines remove --force #{list['id']}"
|
328
|
-
end
|
356
|
+
if exit_code != 0
|
357
|
+
STDERR.puts 'Error performing release'
|
358
|
+
STDERR.puts (exit_code ? {exit_code: exit_code} : event).inspect
|
359
|
+
STDERR.puts "run 'flyctl logs --instance #{machine}' for more information"
|
360
|
+
exit 1
|
329
361
|
end
|
330
362
|
end
|
331
363
|
|
364
|
+
# start proxy, if necessary
|
365
|
+
endpoint = Fly::Machines::fly_api_hostname!
|
366
|
+
|
367
|
+
# stop previous instances - list will fail on first run
|
368
|
+
stdout, stderr, status = Open3.capture3('fly machines list --json')
|
369
|
+
unless stdout.empty?
|
370
|
+
JSON.parse(stdout).each do |list|
|
371
|
+
next if list['id'] == machine or list['state'] == 'destroyed'
|
372
|
+
cmd = "fly machines remove --force #{list['id']}"
|
373
|
+
say_status :run, cmd
|
374
|
+
system cmd
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
332
378
|
# configure sqlite3 (can be overridden by fly.toml)
|
333
379
|
if @sqlite3
|
334
380
|
config[:mounts] = [
|
@@ -354,38 +400,52 @@ module Fly
|
|
354
400
|
end
|
355
401
|
|
356
402
|
# start app
|
403
|
+
machines = {}
|
357
404
|
say_status :fly, "start #{app}"
|
358
|
-
|
359
|
-
|
405
|
+
if not toml['processes']
|
406
|
+
start = Fly::Machines.create_and_start_machine(app, config: config)
|
407
|
+
machines['app'] = start[:id]
|
408
|
+
else
|
409
|
+
config[:env] ||= {}
|
410
|
+
toml['processes'].each do |name, entrypoint|
|
411
|
+
config[:env]['SERVER_COMMAND'] = entrypoint
|
412
|
+
start = Fly::Machines.create_and_start_machine(app, config: config)
|
413
|
+
machines[name] = start[:id]
|
414
|
+
end
|
415
|
+
end
|
360
416
|
|
361
|
-
if
|
417
|
+
if machines.empty?
|
362
418
|
STDERR.puts 'Error starting application'
|
363
419
|
PP.pp start, STDERR
|
364
420
|
exit 1
|
365
421
|
end
|
366
422
|
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
423
|
+
timeout = Time.now + 300
|
424
|
+
while Time.now < timeout and not machines.empty?
|
425
|
+
machines.each do |name, machine|
|
426
|
+
status = Fly::Machines.wait_for_machine app, machine,
|
427
|
+
timeout: 10, status: 'started'
|
428
|
+
machines.delete name if status[:ok]
|
429
|
+
end
|
371
430
|
end
|
372
431
|
|
373
|
-
|
432
|
+
unless machines.empty?
|
433
|
+
STDERR.puts 'Timeout waiting for application to start'
|
434
|
+
end
|
374
435
|
end
|
375
436
|
|
376
437
|
def terraform(app, image)
|
377
|
-
#
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
block.values.first.keys.first == 'fly_machine'}
|
438
|
+
# find first machine using the image ref in terraform config file
|
439
|
+
machine = Fly::HCL.parse(IO.read('main.tf')).
|
440
|
+
map {|block| block.dig(:resource, 'fly_machine')}.compact.
|
441
|
+
find {|machine| machine.values.first[:image] == 'var.image_ref'}
|
442
|
+
if not machine
|
443
|
+
STDERR.puts 'unable to find fly_machine with image = var.image_ref in main.rf'
|
444
|
+
exit 1
|
445
|
+
end
|
386
446
|
|
387
447
|
# extract HCL configuration for the machine
|
388
|
-
config =
|
448
|
+
config = machine.values.first
|
389
449
|
|
390
450
|
# delete HCL specific configuration items
|
391
451
|
%i(services for_each region app name depends_on).each do |key|
|
@@ -407,40 +467,25 @@ module Fly
|
|
407
467
|
config[:env] ||= {}
|
408
468
|
config[:env]['SERVER_COMMAND'] = 'bin/rails fly:release'
|
409
469
|
|
470
|
+
# fill in image
|
471
|
+
config[:image] = image
|
472
|
+
|
410
473
|
# start proxy, if necessary
|
411
474
|
endpoint = Fly::Machines::fly_api_hostname!
|
412
475
|
|
413
|
-
#
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
STDERR.puts 'Error starting release machine'
|
420
|
-
PP.pp start, STDERR
|
421
|
-
exit 1
|
422
|
-
end
|
423
|
-
|
424
|
-
# wait for release to copmlete
|
425
|
-
event = nil
|
426
|
-
90.times do
|
427
|
-
sleep 1
|
428
|
-
status = Fly::Machines.get_a_machine app, machine
|
429
|
-
event = status[:events]&.first
|
430
|
-
break if event && event[:type] == 'exit'
|
476
|
+
# perform release, if necessary
|
477
|
+
if (IO.read('lib/tasks/fly.rake') rescue '') =~ /^\s*task[ \t]*+:?release"?[ \t]*\S/
|
478
|
+
say_status :fly, config[:env]['SERVER_COMMAND']
|
479
|
+
event, exit_code, machine = release(app, config)
|
480
|
+
else
|
481
|
+
exit_code = 0
|
431
482
|
end
|
432
483
|
|
433
|
-
# extract exit code
|
434
|
-
exit_code = event.dig(:request, :exit_event, :exit_code)
|
435
|
-
|
436
484
|
if exit_code == 0
|
437
|
-
# delete release machine
|
438
|
-
Fly::Machines.delete_machine app, machine
|
439
|
-
|
440
485
|
# use terraform apply to deploy
|
441
486
|
ENV['FLY_API_TOKEN'] = `flyctl auth token`.chomp
|
442
487
|
ENV['FLY_HTTP_ENDPOINT'] = endpoint if endpoint
|
443
|
-
system
|
488
|
+
system "terraform apply -auto-approve -var=\"image_ref=#{image}\""
|
444
489
|
else
|
445
490
|
STDERR.puts 'Error performing release'
|
446
491
|
STDERR.puts (exit_code ? {exit_code: exit_code} : event).inspect
|
data/lib/fly.io-rails/scanner.rb
CHANGED
@@ -12,7 +12,9 @@ module Fly
|
|
12
12
|
@postgresql = true
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
gemfile = IO.read('Gemfile') rescue ''
|
16
|
+
@sidekiq = gemfile.include? 'sidekiq'
|
17
|
+
@anycable = gemfile.include? 'anycable'
|
16
18
|
|
17
19
|
@cable = ! Dir['app/channels/*.rb'].empty?
|
18
20
|
|
data/lib/fly.io-rails/utils.rb
CHANGED
data/lib/fly.io-rails/version.rb
CHANGED
@@ -9,7 +9,10 @@ class AppGenerator < Rails::Generators::Base
|
|
9
9
|
class_option :region, type: :array, repeatable: true, default: []
|
10
10
|
class_option :nomad, type: :boolean, default: false
|
11
11
|
|
12
|
+
class_option :anycable, type: :boolean, default: false
|
13
|
+
class_option :avahi, type: :boolean, default: false
|
12
14
|
class_option :litefs, type: :boolean, default: false
|
15
|
+
class_option :redis, type: :boolean, default: false
|
13
16
|
class_option :passenger, type: :boolean, default: false
|
14
17
|
class_option :serverless, type: :boolean, default: false
|
15
18
|
|
@@ -9,6 +9,8 @@ class TerraformGenerator < Rails::Generators::Base
|
|
9
9
|
class_option :region, type: :array, repeatable: true, default: []
|
10
10
|
|
11
11
|
class_option :litefs, type: :boolean, default: false
|
12
|
+
class_option :passenger, type: :boolean, default: false
|
13
|
+
class_option :serverless, type: :boolean, default: false
|
12
14
|
|
13
15
|
def terraform
|
14
16
|
source_paths.push File.expand_path('../templates', __dir__)
|
@@ -20,6 +22,7 @@ class TerraformGenerator < Rails::Generators::Base
|
|
20
22
|
action.generate_toml
|
21
23
|
action.generate_dockerfile
|
22
24
|
action.generate_dockerignore
|
25
|
+
action.generate_nginx_conf
|
23
26
|
action.generate_terraform
|
24
27
|
action.generate_raketask
|
25
28
|
action.generate_procfile
|
@@ -30,9 +30,12 @@ ARG BUNDLER_VERSION=<%= @bundler_version %>
|
|
30
30
|
|
31
31
|
ARG RAILS_ENV=production
|
32
32
|
ENV RAILS_ENV=${RAILS_ENV}
|
33
|
-
|
34
|
-
ENV RAILS_SERVE_STATIC_FILES true
|
33
|
+
<% if @anycable or not @passenger -%>
|
35
34
|
ENV RAILS_LOG_TO_STDOUT true
|
35
|
+
<% end -%>
|
36
|
+
<% unless @passenger -%>
|
37
|
+
ENV RAILS_SERVE_STATIC_FILES true
|
38
|
+
<% end -%>
|
36
39
|
|
37
40
|
ARG BUNDLE_WITHOUT=development:test
|
38
41
|
ARG BUNDLE_PATH=vendor/bundle
|
@@ -81,9 +84,6 @@ RUN gem update --system --no-document && \
|
|
81
84
|
|
82
85
|
COPY Gemfile* ./
|
83
86
|
RUN bundle install && rm -rf vendor/bundle/ruby/*/cache
|
84
|
-
<% if @procs.length > 1 -%>
|
85
|
-
RUN gem install foreman
|
86
|
-
<% end -%>
|
87
87
|
|
88
88
|
<% if @node -%>
|
89
89
|
#######################################################################
|
@@ -113,23 +113,27 @@ FROM flyio/litefs:pr-109 AS litefs
|
|
113
113
|
|
114
114
|
FROM base
|
115
115
|
|
116
|
+
<% if @passenger -%>
|
117
|
+
# add passenger repository
|
118
|
+
RUN apt-get install -y dirmngr gnupg apt-transport-https ca-certificates curl && \
|
119
|
+
curl https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key.txt | \
|
120
|
+
gpg --dearmor > /etc/apt/trusted.gpg.d/phusion.gpg && \
|
121
|
+
sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger bullseye main > /etc/apt/sources.list.d/passenger.list'
|
122
|
+
|
123
|
+
<% end -%>
|
116
124
|
<%
|
117
125
|
@deploy_packages = %w(file vim curl gzip)
|
118
126
|
@deploy_packages += %w(nginx passenger libnginx-mod-http-passenger) if @passenger
|
119
127
|
@deploy_packages << 'postgresql-client' if @postgresql
|
120
128
|
@deploy_packages << 'libsqlite3-0' if @sqlite3
|
121
129
|
@deploy_packages << 'fuse' if @litefs
|
130
|
+
@deploy_packages << 'ruby-foreman' if @procs.length > 1
|
131
|
+
@deploy_packages << 'redis-server' if @redis == :internal
|
132
|
+
@deploy_packages += %w(avahi-daemon avahi-utils libnss-mdns) if @avahi
|
122
133
|
-%>
|
123
134
|
ARG DEPLOY_PACKAGES=<%= @deploy_packages.join(' ').inspect %>
|
124
135
|
ENV DEPLOY_PACKAGES=${DEPLOY_PACKAGES}
|
125
136
|
|
126
|
-
<% if @passenger -%>
|
127
|
-
RUN apt-get install -y dirmngr gnupg apt-transport-https ca-certificates curl && \
|
128
|
-
curl https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key.txt | \
|
129
|
-
gpg --dearmor > /etc/apt/trusted.gpg.d/phusion.gpg && \
|
130
|
-
sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger bullseye main > /etc/apt/sources.list.d/passenger.list'
|
131
|
-
|
132
|
-
<% end -%>
|
133
137
|
RUN --mount=type=cache,id=prod-apt-cache,sharing=locked,target=/var/cache/apt \
|
134
138
|
--mount=type=cache,id=prod-apt-lib,sharing=locked,target=/var/lib/apt \
|
135
139
|
apt-get update -qq && \
|
@@ -137,6 +141,17 @@ RUN --mount=type=cache,id=prod-apt-cache,sharing=locked,target=/var/cache/apt \
|
|
137
141
|
${DEPLOY_PACKAGES} \
|
138
142
|
&& rm -rf /var/lib/apt/lists /var/cache/apt/archives
|
139
143
|
|
144
|
+
<% if @anycable -%>
|
145
|
+
# install anycable
|
146
|
+
RUN curl -L https://github.com/anycable/anycable-go/releases/download/v1.2.1/anycable-go-linux-amd64 -o /usr/local/bin/anycable-go && chmod 755 /usr/local/bin/anycable-go
|
147
|
+
|
148
|
+
<% end -%>
|
149
|
+
<% if @redis == :internal -%>
|
150
|
+
# configure redis
|
151
|
+
RUN sed -i 's/^daemonize yes/daemonize no/' /etc/redis/redis.conf &&\
|
152
|
+
sed -i 's/^logfile/# logfile/' /etc/redis/redis.conf
|
153
|
+
|
154
|
+
<% end -%>
|
140
155
|
# copy installed gems
|
141
156
|
COPY --from=gems /app /app
|
142
157
|
COPY --from=gems /usr/lib/fullstaq-ruby/versions /usr/lib/fullstaq-ruby/versions
|
@@ -158,15 +173,32 @@ ADD config/litefs.yml /etc/litefs.yml
|
|
158
173
|
RUN mkdir /data
|
159
174
|
<% end -%>
|
160
175
|
#######################################################################
|
176
|
+
<% if @avahi -%>
|
177
|
+
|
178
|
+
# configure avahi for ipv6
|
179
|
+
RUN sed -i 's/mdns4_minimal/mdns_minimal/' /etc/nsswitch.conf
|
180
|
+
<% end -%>
|
161
181
|
<% if @passenger -%>
|
162
182
|
|
163
183
|
# configure nginx/passenger
|
164
184
|
COPY config/nginx.conf /etc/nginx/sites-available/rails.conf
|
165
185
|
RUN rm /etc/nginx/sites-enabled/default && \
|
166
|
-
ln -s /etc/nginx/sites-available/rails.conf /etc/nginx/sites-enabled/
|
186
|
+
ln -s /etc/nginx/sites-available/rails.conf /etc/nginx/sites-enabled/ && \
|
187
|
+
sed -i 's/user .*;/user root;/' /etc/nginx/nginx.conf && \
|
188
|
+
sed -i '/^include/i include /etc/nginx/main.d/*.conf;' /etc/nginx/nginx.conf && \
|
189
|
+
mkdir /etc/nginx/main.d && \
|
190
|
+
echo 'env RAILS_MASTER_KEY;' >> /etc/nginx/main.d/env.conf &&\
|
191
|
+
<% if @redis -%>
|
192
|
+
echo 'env REDIS_URL;' >> /etc/nginx/main.d/env.conf &&\
|
193
|
+
<% end -%>
|
194
|
+
<% if @anycable -%>
|
195
|
+
echo 'env ANYCABLE_RPC_HOST;' >> /etc/nginx/main.d/env.conf &&\
|
196
|
+
echo 'env CABLE_URL;' >> /etc/nginx/main.d/env.conf &&\
|
197
|
+
<% end -%>
|
198
|
+
echo 'env RAILS_LOG_TO_STDOUT;' >> /etc/nginx/main.d/env.conf
|
167
199
|
<% if @serverless -%>
|
168
200
|
COPY config/hook_detached_process /etc/nginx/
|
169
|
-
<% end -%>
|
201
|
+
<% end -%>
|
170
202
|
<% end -%>
|
171
203
|
|
172
204
|
# Deploy your application
|
@@ -17,20 +17,46 @@ namespace :fly do
|
|
17
17
|
task :release => 'db:migrate'
|
18
18
|
<%- end -%>
|
19
19
|
|
20
|
+
<% end -%>
|
21
|
+
<% if @avahi -%>
|
22
|
+
task :env do
|
23
|
+
<% if @redis -%>
|
24
|
+
ENV['REDIS_URL'] = "redis://#{ENV['FLY_REGION']}-redis.local:6379/1"
|
25
|
+
<% end -%>
|
26
|
+
<% if @anycable -%>
|
27
|
+
ENV['ANYCABLE_RPC_HOST'] = "#{ENV['FLY_REGION']}-anycable-rpc.local:50051"
|
28
|
+
ENV['CABLE_URL'] = "#{ENV['FLY_REGION']}-anycable-go.local"
|
29
|
+
<% end -%>
|
30
|
+
end
|
31
|
+
|
20
32
|
<% end -%>
|
21
33
|
# SERVER step:
|
22
34
|
# - changes to the filesystem made here are deployed
|
23
35
|
# - full access to secrets, databases
|
24
36
|
# - failures here result in VM being stated, shutdown, and rolled back
|
25
37
|
# to last successful deploy (if any).
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
38
|
+
<%= begin
|
39
|
+
deps = [:swapfile]
|
40
|
+
deps << 'db:migrate' if @sqlite3
|
41
|
+
deps = deps.first if deps.length == 1
|
42
|
+
|
43
|
+
if @procs.length > 1 ? ':server, [:formation]' : ':server'
|
44
|
+
"task :server, [:formation] => #{deps.inspect} do |task, args|"
|
45
|
+
else
|
46
|
+
"task :server => #{deps.inspect} do"
|
47
|
+
end
|
48
|
+
end %>
|
31
49
|
<%- if @procs.length > 1 -%>
|
50
|
+
formation = args[:formation] || <%= @procs.keys.map {|key| "#{key}=1"}.join(';').inspect %>
|
51
|
+
formation.gsub! ';', ','
|
52
|
+
<%- if @avahi -%>
|
53
|
+
Rake::Task['fly:avahi_publish'].invoke(formation)
|
54
|
+
<%- end -%>
|
32
55
|
Bundler.with_original_env do
|
33
|
-
|
56
|
+
<%- if @avahi -%>
|
57
|
+
# Rake::Task['fly:env'].invoke
|
58
|
+
<%- end -%>
|
59
|
+
sh "foreman start --procfile=Procfile.fly --formation=#{formation}"
|
34
60
|
end
|
35
61
|
<%- else -%>
|
36
62
|
sh <%= @procs.values.first.inspect %>
|
@@ -53,5 +79,8 @@ namespace :fly do
|
|
53
79
|
sh 'mkswap /swapfile'
|
54
80
|
sh 'echo 10 > /proc/sys/vm/swappiness'
|
55
81
|
sh 'swapon /swapfile'
|
82
|
+
<% if @redis == :internal -%>
|
83
|
+
sh 'echo 1 > /proc/sys/vm/overcommit_memory'
|
84
|
+
<% end -%>
|
56
85
|
end
|
57
86
|
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
app = "<%= @app %>"
|
2
2
|
kill_signal = "SIGINT"
|
3
3
|
kill_timeout = 5
|
4
|
+
<% unless @avahi -%>
|
4
5
|
processes = []
|
6
|
+
<% end -%>
|
5
7
|
|
6
8
|
[build]
|
7
9
|
[build.args]
|
@@ -23,6 +25,11 @@ processes = []
|
|
23
25
|
<% else -%>
|
24
26
|
DATABASE_URL = "sqlite3:///mnt/volume/production.sqlite3"
|
25
27
|
<% end -%>
|
28
|
+
<% if @avahi -%>
|
29
|
+
|
30
|
+
[processes]
|
31
|
+
app = "bin/rails fly:server[<%= @procs.keys.map {|key| "#{key}=1"}.join(';') %>]"
|
32
|
+
<% end -%>
|
26
33
|
|
27
34
|
[mounts]
|
28
35
|
source = <%= "#{app.gsub('-', '_')}_volume".inspect %>
|
@@ -3,9 +3,5 @@
|
|
3
3
|
status = `passenger-status`
|
4
4
|
|
5
5
|
processes = status[/^Processes\s*:\s*(\d*)/, 1].to_i
|
6
|
-
<% if @cable -%>
|
7
|
-
cable = status[/^<%= @app %>-cable.*?\n\n/m]
|
8
|
-
processes -= 1 if cable and cable =~ /Sessions:\s*[1-9]/
|
9
|
-
<% end -%>
|
10
6
|
|
11
|
-
system 'nginx -s stop' if processes == 0
|
7
|
+
system 'nginx -s stop' if processes == 0
|
@@ -2,11 +2,16 @@ terraform {
|
|
2
2
|
required_providers {
|
3
3
|
fly = {
|
4
4
|
source = "fly-apps/fly"
|
5
|
-
version = "0.0.
|
5
|
+
version = "0.0.20"
|
6
6
|
}
|
7
7
|
}
|
8
8
|
}
|
9
9
|
|
10
|
+
variable "image_ref" {
|
11
|
+
type = string
|
12
|
+
description = "docker images containing the application"
|
13
|
+
}
|
14
|
+
|
10
15
|
/* uncomment if you want an internal tunnel
|
11
16
|
provider "fly" {
|
12
17
|
useinternaltunnel = true
|
@@ -49,7 +54,7 @@ resource "fly_machine" "<%= @appName %>Machine" {
|
|
49
54
|
|
50
55
|
app = <%= @app.inspect %>
|
51
56
|
name = "<%= @app %>-${each.value}"
|
52
|
-
image =
|
57
|
+
image = var.image_ref
|
53
58
|
|
54
59
|
# Scale application resources
|
55
60
|
cpus = 1
|
@@ -4,26 +4,44 @@ passenger_min_instances 0;
|
|
4
4
|
passenger_pool_idle_time 300;
|
5
5
|
|
6
6
|
<% end -%>
|
7
|
+
passenger_log_file /dev/stdout;
|
8
|
+
passenger_default_user root;
|
9
|
+
|
7
10
|
server {
|
8
|
-
listen 8080;
|
11
|
+
listen 8080 default_server;
|
12
|
+
listen [::]:8080 default_server;
|
9
13
|
server_name <%= @app %>.fly.dev;
|
10
14
|
root /app/public;
|
11
15
|
|
16
|
+
access_log /dev/stdout;
|
17
|
+
error_log /dev/stdout info;
|
18
|
+
|
12
19
|
passenger_enabled on;
|
13
20
|
passenger_ruby /usr/lib/fullstaq-ruby/versions/<%= @ruby_version %>-jemalloc/bin/ruby;
|
14
21
|
|
15
|
-
<% if @
|
16
|
-
location / {
|
17
|
-
|
18
|
-
|
22
|
+
<% if @anycable -%>
|
23
|
+
location /cable {
|
24
|
+
proxy_pass http://localhost:8082/cable;
|
25
|
+
proxy_http_version 1.1;
|
26
|
+
proxy_set_header Upgrade $http_upgrade;
|
27
|
+
proxy_set_header Connection "Upgrade";
|
28
|
+
proxy_set_header Host $host;
|
29
|
+
}
|
19
30
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
31
|
+
<% elsif @cable -%>
|
32
|
+
location /cable {
|
33
|
+
passenger_app_group_name <%= @app %>-cable;
|
34
|
+
passenger_force_max_concurrent_requests_per_process 0;
|
35
|
+
}
|
24
36
|
|
25
37
|
<% end -%>
|
38
|
+
location / {
|
39
|
+
passenger_app_group_name <%= @app %>;
|
40
|
+
passenger_env_var RAILS_SERVE_STATIC_FILES true;
|
41
|
+
passenger_env_var RAILS_LOG_TO_STDOUT true;
|
42
|
+
}
|
43
|
+
|
26
44
|
# Nginx has a default limit of 1 MB for request bodies, which also applies
|
27
45
|
# to file uploads. The following line enables uploads of up to 50 MB:
|
28
46
|
client_max_body_size 50M;
|
29
|
-
}
|
47
|
+
}
|
data/lib/tasks/fly.rake
CHANGED
@@ -37,6 +37,42 @@ namespace :fly do
|
|
37
37
|
action.generate_ipv6 if @app
|
38
38
|
action.deploy(app, image)
|
39
39
|
end
|
40
|
+
|
41
|
+
JSON.parse(`fly apps list --json`).each do |info|
|
42
|
+
if info['Name'] == app
|
43
|
+
60.times do
|
44
|
+
response = Net::HTTP.get_response(URI::HTTPS.build(host: info['Hostname']))
|
45
|
+
puts "Server status: #{response.code} #{response.message}"
|
46
|
+
break
|
47
|
+
rescue Errno::ECONNRESET
|
48
|
+
sleep 0.5
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
desc 'dbus daemon - used for IPC'
|
55
|
+
task :dbus_deamon do
|
56
|
+
IO.write '/var/lib/dbus/machine-id', `hostname`
|
57
|
+
mkdir_p '/var/run/dbus'
|
58
|
+
sh 'dbus-daemon --config-file=/usr/share/dbus-1/system.conf --print-address'
|
59
|
+
end
|
60
|
+
|
61
|
+
desc 'Zeroconf/avahi/bonjour discovery'
|
62
|
+
task :avahi_publish, [:formation] => :dbus_deamon do |task, args|
|
63
|
+
pids = []
|
64
|
+
pids << spawn('avahi-daemon')
|
65
|
+
sleep 0.1
|
66
|
+
|
67
|
+
ip = IPSocket.getaddress(Socket.gethostname)
|
68
|
+
args[:formation].scan(/([-\w]+)=(\d+)/).each do |name, count|
|
69
|
+
next if count.to_i == 0
|
70
|
+
pids << spawn("avahi-publish -a -R #{ENV['FLY_REGION']}-#{name}.local #{ip}")
|
71
|
+
end
|
72
|
+
|
73
|
+
at_exit do
|
74
|
+
pids.each {|pid| Process.kill 7, pid}
|
75
|
+
end
|
40
76
|
end
|
41
77
|
end
|
42
78
|
|
data/lib/tasks/mock.rake
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'rack/handler/puma'
|
3
|
+
|
4
|
+
namespace :mock do
|
5
|
+
desc 'Mock server - useful for debugging startup issues'
|
6
|
+
task :server do
|
7
|
+
handler = Rack::Handler::Puma
|
8
|
+
|
9
|
+
class RackApp
|
10
|
+
def call(env)
|
11
|
+
[200, {"Content-Type" => "text/plain"}, ["Hello from Fly.io"]]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
handler.run RackApp.new, Port: ENV['PORT'] || 8080
|
16
|
+
end
|
17
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fly.io-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Ruby
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-10-
|
11
|
+
date: 2022-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fly-ruby
|
@@ -73,11 +73,12 @@ files:
|
|
73
73
|
- lib/generators/templates/nginx.conf.erb
|
74
74
|
- lib/generators/templates/patches/action_cable.rb
|
75
75
|
- lib/tasks/fly.rake
|
76
|
-
|
76
|
+
- lib/tasks/mock.rake
|
77
|
+
homepage: https://github.com/rubys/fly.io-rails
|
77
78
|
licenses:
|
78
79
|
- Apache-2.0
|
79
80
|
metadata:
|
80
|
-
homepage_uri: https://github.com/rubys/fly-
|
81
|
+
homepage_uri: https://github.com/rubys/fly.io-rails
|
81
82
|
post_install_message:
|
82
83
|
rdoc_options: []
|
83
84
|
require_paths:
|