fly.io-rails 0.1.0-x64-mingw32 → 0.1.2-x64-mingw32
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.
- checksums.yaml +4 -4
- data/exe/x64-mingw32/flyctl.exe +0 -0
- data/lib/fly.io-rails/actions.rb +168 -75
- data/lib/fly.io-rails/dsl.rb +79 -0
- data/lib/fly.io-rails/machines.rb +2 -2
- data/lib/fly.io-rails/scanner.rb +26 -0
- data/lib/fly.io-rails/utils.rb +27 -8
- data/lib/fly.io-rails/version.rb +1 -1
- data/lib/generators/fly/app_generator.rb +29 -0
- data/lib/generators/fly/terraform_generator.rb +29 -0
- data/lib/generators/templates/fly.rake.erb +12 -0
- data/lib/generators/templates/fly.rb.erb +25 -0
- data/lib/generators/templates/main.tf.erb +9 -1
- data/lib/generators/templates/patches/action_cable.rb +20 -0
- data/lib/tasks/fly.rake +3 -8
- metadata +8 -3
- data/lib/generators/terraform_generator.rb +0 -52
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cec3ca4f27cbb576d74009dcdea78a60113a091c85b833efb1f83388bfd37475
|
4
|
+
data.tar.gz: 702966a87970bca9d3192dbc8cd76cb94bb5fdf0cfa25f331ea290a02f08f097
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17a5d4feb8fdfe76af46edb6b6f416c5387aab1d01a6a2e8dcf10d9c37925eafd0a17043f67b5faf3a6d056f58bacb147494964142957e3848199caf0bcec792
|
7
|
+
data.tar.gz: bf8f63c0bb7d6d890a3dbb1f1b8f5b4fb2190277ef606e6b2bca2016c58c649c1c474ba0c6c48be0dc99e2bbda4a6fbfc9e3cd35248a49617aff0d4f4a00d628
|
data/exe/x64-mingw32/flyctl.exe
CHANGED
Binary file
|
data/lib/fly.io-rails/actions.rb
CHANGED
@@ -1,17 +1,21 @@
|
|
1
|
+
require 'open3'
|
1
2
|
require 'thor'
|
2
3
|
require 'active_support'
|
3
4
|
require 'active_support/core_ext/string/inflections'
|
4
5
|
require 'fly.io-rails/machines'
|
5
6
|
require 'fly.io-rails/utils'
|
7
|
+
require 'fly.io-rails/dsl'
|
8
|
+
require 'fly.io-rails/scanner'
|
6
9
|
|
7
10
|
module Fly
|
8
11
|
class Actions < Thor::Group
|
9
12
|
include Thor::Actions
|
10
13
|
include Thor::Base
|
11
14
|
include Thor::Shell
|
15
|
+
include Fly::Scanner
|
12
16
|
attr_accessor :options
|
13
17
|
|
14
|
-
def initialize(app = nil)
|
18
|
+
def initialize(app = nil, regions = nil)
|
15
19
|
self.app = app if app
|
16
20
|
|
17
21
|
@ruby_version = RUBY_VERSION
|
@@ -20,10 +24,25 @@ module Fly
|
|
20
24
|
@yarn = File.exist? 'yarn.lock'
|
21
25
|
@node_version = @node ? `node --version`.chomp.sub(/^v/, '') : '16.17.0'
|
22
26
|
@org = Fly::Machines.org
|
23
|
-
@regions = []
|
24
27
|
|
25
28
|
@options = {}
|
26
29
|
@destination_stack = [Dir.pwd]
|
30
|
+
|
31
|
+
if !regions or regions.empty?
|
32
|
+
@regions = JSON.parse(`flyctl regions list --json --app #{app}`)['Regions'].
|
33
|
+
map {|region| region['Code']} rescue []
|
34
|
+
else
|
35
|
+
@regions = regions
|
36
|
+
end
|
37
|
+
|
38
|
+
@region = @regions.first || 'iad'
|
39
|
+
|
40
|
+
@config = Fly::DSL::Config.new
|
41
|
+
if File.exist? 'config/fly.rb'
|
42
|
+
@config.instance_eval IO.read('config/fly.rb')
|
43
|
+
end
|
44
|
+
|
45
|
+
scan_rails_app
|
27
46
|
end
|
28
47
|
|
29
48
|
def app
|
@@ -43,6 +62,11 @@ module Fly
|
|
43
62
|
template 'fly.toml.erb', 'fly.toml'
|
44
63
|
end
|
45
64
|
|
65
|
+
def generate_fly_config
|
66
|
+
app
|
67
|
+
template 'fly.rb.erb', 'config/fly.rb'
|
68
|
+
end
|
69
|
+
|
46
70
|
def generate_dockerfile
|
47
71
|
app
|
48
72
|
template 'Dockerfile.erb', 'Dockerfile'
|
@@ -63,11 +87,28 @@ module Fly
|
|
63
87
|
template 'fly.rake.erb', 'lib/tasks/fly.rake'
|
64
88
|
end
|
65
89
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
90
|
+
def generate_key
|
91
|
+
credentials = nil
|
92
|
+
if File.exist? 'config/credentials/production.key'
|
93
|
+
credentials = 'config/credentials/production.key'
|
94
|
+
elsif File.exist? 'config/master.key'
|
95
|
+
credentials = 'config/master.key'
|
96
|
+
end
|
97
|
+
|
98
|
+
if credentials
|
99
|
+
say_status :run, "flyctl secrets set --stage RAILS_MASTER_KEY from #{credentials}"
|
100
|
+
system "flyctl secrets set --stage RAILS_MASTER_KEY=#{IO.read(credentials).chomp}"
|
101
|
+
puts
|
102
|
+
end
|
103
|
+
|
104
|
+
ENV['FLY_API_TOKEN'] = `flyctl auth token`
|
105
|
+
end
|
106
|
+
|
107
|
+
def generate_patches
|
108
|
+
if @redis_cable and not File.exist? 'config/initializers/action_cable.rb'
|
109
|
+
app
|
110
|
+
template 'patches/action_cable.rb', 'config/initializers/action_cable.rb'
|
111
|
+
end
|
71
112
|
end
|
72
113
|
|
73
114
|
def generate_ipv4
|
@@ -85,7 +126,7 @@ module Fly
|
|
85
126
|
def create_volume(app, region, size)
|
86
127
|
volume = "#{app.gsub('-', '_')}_volume"
|
87
128
|
volumes = JSON.parse(`flyctl volumes list --json`).
|
88
|
-
map {|volume| volume['
|
129
|
+
map {|volume| volume['Name']}
|
89
130
|
|
90
131
|
unless volumes.include? volume
|
91
132
|
cmd = "flyctl volumes create #{volume} --app #{app} --region #{region} --size #{size}"
|
@@ -97,71 +138,96 @@ module Fly
|
|
97
138
|
end
|
98
139
|
|
99
140
|
def create_postgres(app, org, region, vm_size, volume_size, cluster_size)
|
100
|
-
cmd = "
|
141
|
+
cmd = "flyctl postgres create --name #{app}-db --org #{org} --region #{region} --vm-size #{vm_size} --volume-size #{volume_size} --initial-cluster-size #{cluster_size}"
|
101
142
|
say_status :run, cmd
|
102
143
|
output = FlyIoRails::Utils.tee(cmd)
|
103
144
|
output[%r{postgres://\S+}]
|
104
145
|
end
|
105
146
|
|
147
|
+
def create_redis(app, org, region, eviction)
|
148
|
+
# see if redis is already defined
|
149
|
+
name = `flyctl redis list`.lines[1..-2].map(&:split).
|
150
|
+
find {|tokens| tokens[1] == org}&.first
|
151
|
+
|
152
|
+
if name
|
153
|
+
secret = `flyctl redis status #{name}`[%r{redis://\S+}]
|
154
|
+
return secret if secret
|
155
|
+
end
|
156
|
+
|
157
|
+
# create a new redis
|
158
|
+
cmd = "flyctl redis create --org #{org} --name #{app}-redis --region #{region} --no-replicas #{eviction} --plan #{@config.redis.plan}"
|
159
|
+
say_status :run, cmd
|
160
|
+
output = FlyIoRails::Utils.tee(cmd)
|
161
|
+
output[%r{redis://\S+}]
|
162
|
+
end
|
163
|
+
|
106
164
|
def release(app, config)
|
107
|
-
start = Fly::Machines.
|
165
|
+
start = Fly::Machines.create_and_start_machine(app, config: config)
|
108
166
|
machine = start[:id]
|
109
167
|
|
110
168
|
if !machine
|
111
|
-
|
112
|
-
|
113
|
-
|
169
|
+
STDERR.puts 'Error starting release machine'
|
170
|
+
PP.pp start, STDERR
|
171
|
+
exit 1
|
114
172
|
end
|
115
173
|
|
174
|
+
status = Fly::Machines.wait_for_machine app, machine,
|
175
|
+
timeout: 60, state: 'started'
|
176
|
+
|
116
177
|
# wait for release to copmlete
|
117
178
|
status = nil
|
118
179
|
5.times do
|
119
|
-
|
120
|
-
timeout: 60,
|
121
|
-
|
180
|
+
status = Fly::Machines.wait_for_machine app, machine,
|
181
|
+
timeout: 60, state: 'stopped'
|
182
|
+
return machine if status[:ok]
|
183
|
+
end
|
184
|
+
|
185
|
+
# wait for release to copmlete
|
186
|
+
event = nil
|
187
|
+
90.times do
|
188
|
+
sleep 1
|
189
|
+
status = Fly::Machines.get_a_machine app, machine
|
190
|
+
event = status[:events]&.first
|
191
|
+
return machine if event && event[:type] == 'exit'
|
122
192
|
end
|
123
193
|
|
124
|
-
STDERR.puts
|
194
|
+
STDERR.puts event.to_json
|
125
195
|
exit 1
|
126
196
|
end
|
127
197
|
|
128
198
|
def deploy(app, image)
|
129
|
-
regions = JSON.parse(`flyctl regions list --json`)['Regions'].
|
130
|
-
map {|region| region['Code']} rescue []
|
131
|
-
region = regions.first || 'iad'
|
132
199
|
|
133
|
-
secrets = JSON.parse(`
|
200
|
+
secrets = JSON.parse(`flyctl secrets list --json`).
|
134
201
|
map {|secret| secret["Name"]}
|
135
202
|
|
136
203
|
config = {
|
137
|
-
region: region,
|
204
|
+
region: @region,
|
138
205
|
app: app,
|
139
206
|
name: "#{app}-machine",
|
140
207
|
image: image,
|
141
208
|
guest: {
|
142
|
-
cpus:
|
143
|
-
cpu_kind:
|
144
|
-
memory_mb:
|
209
|
+
cpus: @config.machine.cpus,
|
210
|
+
cpu_kind: @config.machine.cpu_kind,
|
211
|
+
memory_mb: @config.machine.memory_mb
|
145
212
|
},
|
146
213
|
services: [
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
214
|
+
{
|
215
|
+
ports: [
|
216
|
+
{port: 443, handlers: ["tls", "http"]},
|
217
|
+
{port: 80, handlers: ["http"]}
|
218
|
+
],
|
219
|
+
protocol: "tcp",
|
220
|
+
internal_port: 8080
|
221
|
+
}
|
155
222
|
]
|
156
223
|
}
|
157
224
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
dig('production', 'adapter') rescue nil
|
225
|
+
unless secrets.include? 'RAILS_MASTER_KEY'
|
226
|
+
generate_key
|
227
|
+
end
|
162
228
|
|
163
|
-
if
|
164
|
-
volume = create_volume(app, region,
|
229
|
+
if @sqlite3
|
230
|
+
volume = create_volume(app, @region, @config.sqlite3.size)
|
165
231
|
|
166
232
|
config[:mounts] = [
|
167
233
|
{ volume: volume, path: '/mnt/volume' }
|
@@ -170,12 +236,30 @@ module Fly
|
|
170
236
|
config[:env] = {
|
171
237
|
"DATABASE_URL" => "sqlite3:///mnt/volume/production.sqlite3"
|
172
238
|
}
|
173
|
-
elsif
|
174
|
-
secret = create_postgres(app, @org, region,
|
239
|
+
elsif @postgresql and not secrets.include? 'DATABASE_URL'
|
240
|
+
secret = create_postgres(app, @org, @region,
|
241
|
+
@config.postgres.vm_size,
|
242
|
+
@config.postgres.volume_size,
|
243
|
+
@config.postgres.initial_cluster_size)
|
244
|
+
|
245
|
+
if secret
|
246
|
+
cmd = "flyctl secrets set --stage DATABASE_URL=#{secret}"
|
247
|
+
say_status :run, cmd
|
248
|
+
system cmd
|
249
|
+
end
|
250
|
+
end
|
175
251
|
|
176
|
-
|
177
|
-
|
178
|
-
|
252
|
+
if @redis and not secrets.include? 'REDIS_URL'
|
253
|
+
# Set eviction policy to true if a cache provider, else false.
|
254
|
+
eviction = @redis_cache ? '--enable-eviction' : '--disable-eviction'
|
255
|
+
|
256
|
+
secret = create_redis(app, @org, @region, eviction)
|
257
|
+
|
258
|
+
if secret
|
259
|
+
cmd = "flyctl secrets set --stage REDIS_URL=#{secret}"
|
260
|
+
say_status :run, cmd
|
261
|
+
system cmd
|
262
|
+
end
|
179
263
|
end
|
180
264
|
|
181
265
|
# build config for release machine, overriding server command
|
@@ -192,21 +276,30 @@ module Fly
|
|
192
276
|
# start proxy, if necessary
|
193
277
|
endpoint = Fly::Machines::fly_api_hostname!
|
194
278
|
|
279
|
+
# stop previous instances - list will fail on first run
|
280
|
+
stdout, stderr, status = Open3.capture3('fly machines list --json')
|
281
|
+
unless stdout.empty?
|
282
|
+
JSON.parse(stdout).each do |list|
|
283
|
+
next if list['id'] == machine
|
284
|
+
system "fly machines remove --force #{list['id']}"
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
195
288
|
# start app
|
196
289
|
say_status :fly, "start #{app}"
|
197
|
-
start = Fly::Machines.
|
290
|
+
start = Fly::Machines.create_and_start_machine(app, config: config)
|
198
291
|
machine = start[:id]
|
199
292
|
|
200
293
|
if !machine
|
201
|
-
|
202
|
-
|
203
|
-
|
294
|
+
STDERR.puts 'Error starting application'
|
295
|
+
PP.pp start, STDERR
|
296
|
+
exit 1
|
204
297
|
end
|
205
298
|
|
206
299
|
5.times do
|
207
|
-
|
300
|
+
status = Fly::Machines.wait_for_machine app, machine,
|
208
301
|
timeout: 60, status: 'started'
|
209
|
-
|
302
|
+
return if status[:ok]
|
210
303
|
end
|
211
304
|
|
212
305
|
STDERR.puts 'Timeout waiting for application to start'
|
@@ -220,22 +313,22 @@ module Fly
|
|
220
313
|
|
221
314
|
# find first machine in terraform config file
|
222
315
|
machines = Fly::HCL.parse(IO.read('main.tf')).find {|block|
|
223
|
-
|
224
|
-
|
316
|
+
block.keys.first == :resource and
|
317
|
+
block.values.first.keys.first == 'fly_machine'}
|
225
318
|
|
226
319
|
# extract HCL configuration for the machine
|
227
320
|
config = machines.values.first.values.first.values.first
|
228
321
|
|
229
322
|
# delete HCL specific configuration items
|
230
323
|
%i(services for_each region app name depends_on).each do |key|
|
231
|
-
|
324
|
+
config.delete key
|
232
325
|
end
|
233
326
|
|
234
327
|
# move machine configuration into guest object
|
235
328
|
config[:guest] = {
|
236
|
-
|
237
|
-
|
238
|
-
|
329
|
+
cpus: config.delete(:cpus),
|
330
|
+
memory_mb: config.delete(:memorymb),
|
331
|
+
cpu_kind: config.delete(:cputype)
|
239
332
|
}
|
240
333
|
|
241
334
|
# release machines should have no services or mounts
|
@@ -251,40 +344,40 @@ module Fly
|
|
251
344
|
|
252
345
|
# start release machine
|
253
346
|
STDERR.puts "--> #{config[:env]['SERVER_COMMAND']}"
|
254
|
-
start = Fly::Machines.
|
347
|
+
start = Fly::Machines.create_and_start_machine(app, config: config)
|
255
348
|
machine = start[:id]
|
256
349
|
|
257
350
|
if !machine
|
258
|
-
|
259
|
-
|
260
|
-
|
351
|
+
STDERR.puts 'Error starting release machine'
|
352
|
+
PP.pp start, STDERR
|
353
|
+
exit 1
|
261
354
|
end
|
262
355
|
|
263
356
|
# wait for release to copmlete
|
264
357
|
event = nil
|
265
358
|
90.times do
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
359
|
+
sleep 1
|
360
|
+
status = Fly::Machines.get_a_machine app, machine
|
361
|
+
event = status[:events]&.first
|
362
|
+
break if event && event[:type] == 'exit'
|
270
363
|
end
|
271
364
|
|
272
365
|
# extract exit code
|
273
366
|
exit_code = event.dig(:request, :exit_event, :exit_code)
|
274
|
-
|
367
|
+
|
275
368
|
if exit_code == 0
|
276
|
-
|
277
|
-
|
369
|
+
# delete release machine
|
370
|
+
Fly::Machines.delete_machine app, machine
|
278
371
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
372
|
+
# use terraform apply to deploy
|
373
|
+
ENV['FLY_API_TOKEN'] = `flyctl auth token`.chomp
|
374
|
+
ENV['FLY_HTTP_ENDPOINT'] = endpoint if endpoint
|
375
|
+
system 'terraform apply -auto-approve'
|
283
376
|
else
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
377
|
+
STDERR.puts 'Error performing release'
|
378
|
+
STDERR.puts (exit_code ? {exit_code: exit_code} : event).inspect
|
379
|
+
STDERR.puts "run 'flyctl logs --instance #{machine}' for more information"
|
380
|
+
exit 1
|
288
381
|
end
|
289
382
|
end
|
290
383
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Fly
|
2
|
+
module DSL
|
3
|
+
class Base
|
4
|
+
def initialize
|
5
|
+
@value = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.option name, default=nil
|
9
|
+
@options ||= {}
|
10
|
+
@options[name] = default
|
11
|
+
|
12
|
+
define_method name do |*args|
|
13
|
+
if args.length == 1
|
14
|
+
@value[name] = args.first
|
15
|
+
elsif args.length > 1
|
16
|
+
raise ArgumentError.new("wrong number of arguments (given #{args.length}, expected 0..1)")
|
17
|
+
end
|
18
|
+
|
19
|
+
@value.include?(name) ? @value[name] : default
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.options
|
24
|
+
@options ||= {}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#############################################################
|
29
|
+
|
30
|
+
class Machine < Base
|
31
|
+
option :cpus, 1
|
32
|
+
option :cpu_kind, 'shared'
|
33
|
+
option :memory_mb, 256
|
34
|
+
end
|
35
|
+
|
36
|
+
class Postgres < Base
|
37
|
+
option :vm_size, 'shared-cpu-1x'
|
38
|
+
option :volume_size, 1
|
39
|
+
option :initial_cluster_size, 1
|
40
|
+
end
|
41
|
+
|
42
|
+
class Redis < Base
|
43
|
+
option :plan, "Free"
|
44
|
+
end
|
45
|
+
|
46
|
+
class Sqlite3 < Base
|
47
|
+
option :size, 3
|
48
|
+
end
|
49
|
+
|
50
|
+
#############################################################
|
51
|
+
|
52
|
+
class Config
|
53
|
+
@@blocks = {}
|
54
|
+
|
55
|
+
def initialize
|
56
|
+
@config = {}
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.block name, kind
|
60
|
+
@@blocks[name] = kind
|
61
|
+
|
62
|
+
define_method name do |&block|
|
63
|
+
@config[name] ||= kind.new
|
64
|
+
@config[name].instance_eval(&block) if block
|
65
|
+
@config[name]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.blocks
|
70
|
+
@@blocks
|
71
|
+
end
|
72
|
+
|
73
|
+
block :machine, Machine
|
74
|
+
block :postgres, Postgres
|
75
|
+
block :redis, Redis
|
76
|
+
block :sqlite3, Sqlite3
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -80,7 +80,7 @@ module Fly
|
|
80
80
|
get "/v1/apps/#{app}"
|
81
81
|
end
|
82
82
|
|
83
|
-
#
|
83
|
+
# create_and_start_machine 'user-functions', name: 'quirky_machine', config: {
|
84
84
|
# image: 'flyio/fastify-functions',
|
85
85
|
# env: {'APP_ENV' => 'production'},
|
86
86
|
# services: [
|
@@ -94,7 +94,7 @@ module Fly
|
|
94
94
|
# }
|
95
95
|
# ]
|
96
96
|
# }
|
97
|
-
def self.
|
97
|
+
def self.create_and_start_machine app, options
|
98
98
|
post "/v1/apps/#{app}/machines", options
|
99
99
|
end
|
100
100
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Fly
|
2
|
+
module Scanner
|
3
|
+
# scan for major features - things that if present will likely affect
|
4
|
+
# more than one artifact that is generated.
|
5
|
+
def scan_rails_app
|
6
|
+
database = YAML.load_file('config/database.yml').
|
7
|
+
dig('production', 'adapter') rescue nil
|
8
|
+
|
9
|
+
if database == 'sqlite3'
|
10
|
+
@sqlite3 = true
|
11
|
+
elsif database == 'postgresql'
|
12
|
+
@postgresql = true
|
13
|
+
end
|
14
|
+
|
15
|
+
if (YAML.load_file('config/cable.yml').dig('production', 'adapter') rescue false)
|
16
|
+
@redis_cable = true
|
17
|
+
end
|
18
|
+
|
19
|
+
if (IO.read('config/environments/production.rb') =~ /redis/i rescue false)
|
20
|
+
@redis_cache = true
|
21
|
+
end
|
22
|
+
|
23
|
+
@redis = @redis_cable || @redis_cache
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/fly.io-rails/utils.rb
CHANGED
@@ -4,7 +4,7 @@ module FlyIoRails
|
|
4
4
|
module Utils
|
5
5
|
|
6
6
|
def tee cmd
|
7
|
-
say_status :run, cmd
|
7
|
+
say_status :run, cmd if defined? say_status
|
8
8
|
FlyIoRails::Utils.tee cmd
|
9
9
|
end
|
10
10
|
|
@@ -12,21 +12,40 @@ module FlyIoRails
|
|
12
12
|
data = []
|
13
13
|
|
14
14
|
begin
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
PTY.spawn( cmd ) do |stdin, stdout, pid|
|
16
|
+
begin
|
17
|
+
# Do stuff with the output here. Just printing to show it works
|
18
|
+
stdin.each do |line|
|
19
19
|
print line
|
20
20
|
data << line
|
21
21
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
rescue Errno::EIO
|
23
|
+
end
|
24
|
+
end
|
25
25
|
rescue PTY::ChildExited
|
26
26
|
end
|
27
27
|
|
28
28
|
data.join
|
29
29
|
end
|
30
30
|
|
31
|
+
def create_app(name=nil, org='personal', regions=[])
|
32
|
+
cmd = if name
|
33
|
+
"flyctl apps create #{name.inspect} --org #{org.inspect} --machines"
|
34
|
+
else
|
35
|
+
"flyctl apps create --generate-name --org #{org.inspect} --machines"
|
36
|
+
end
|
37
|
+
|
38
|
+
output = tee cmd
|
39
|
+
exit 1 unless output =~ /^New app created: /
|
40
|
+
|
41
|
+
@app = output.split.last
|
42
|
+
template 'fly.toml.erb', 'fly.toml' if defined? template # rake tasks are on their own
|
43
|
+
|
44
|
+
unless regions.empty?
|
45
|
+
@regions = regions.flatten
|
46
|
+
end
|
47
|
+
|
48
|
+
@app
|
49
|
+
end
|
31
50
|
end
|
32
51
|
end
|
data/lib/fly.io-rails/version.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'fly.io-rails/actions'
|
2
|
+
|
3
|
+
module Fly::Generators
|
4
|
+
class AppGenerator < Rails::Generators::Base
|
5
|
+
include FlyIoRails::Utils
|
6
|
+
|
7
|
+
class_option :name, type: :string, required: false
|
8
|
+
class_option :org, type: :string, default: 'personal'
|
9
|
+
class_option :region, type: :array, repeatable: true, default: []
|
10
|
+
|
11
|
+
def generate_app
|
12
|
+
source_paths.push File.expand_path('../templates', __dir__)
|
13
|
+
|
14
|
+
create_app(options[:name], options[:org], options)
|
15
|
+
|
16
|
+
action = Fly::Actions.new(@app, options[:region])
|
17
|
+
|
18
|
+
action.generate_fly_config unless File.exist? 'config/fly.rb'
|
19
|
+
action.generate_dockerfile unless File.exist? 'Dockerfile'
|
20
|
+
action.generate_dockerignore unless File.exist? '.dockerignore'
|
21
|
+
action.generate_raketask unless File.exist? 'lib/tasks/fly.rake'
|
22
|
+
action.generate_patches
|
23
|
+
action.generate_ipv4
|
24
|
+
action.generate_ipv6
|
25
|
+
action.generate_key
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'fly.io-rails/actions'
|
2
|
+
|
3
|
+
module Fly::Generators
|
4
|
+
class TerraformGenerator < Rails::Generators::Base
|
5
|
+
include FlyIoRails::Utils
|
6
|
+
|
7
|
+
class_option :name, type: :string, required: false
|
8
|
+
class_option :org, type: :string, default: 'personal'
|
9
|
+
class_option :region, type: :array, repeatable: true, default: []
|
10
|
+
|
11
|
+
def terraform
|
12
|
+
source_paths.push File.expand_path('../templates', __dir__)
|
13
|
+
|
14
|
+
create_app(options[:name], options[:org], options[:region])
|
15
|
+
|
16
|
+
action = Fly::Actions.new(@app, options[:region])
|
17
|
+
|
18
|
+
action.generate_dockerfile
|
19
|
+
action.generate_dockerignore
|
20
|
+
action.generate_terraform
|
21
|
+
action.generate_raketask
|
22
|
+
action.generate_patches
|
23
|
+
|
24
|
+
action.generate_key
|
25
|
+
|
26
|
+
tee 'terraform init'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -10,14 +10,22 @@ namespace :fly do
|
|
10
10
|
# - changes to the filesystem made here are DISCARDED
|
11
11
|
# - full access to secrets, databases
|
12
12
|
# - failures here prevent deployment
|
13
|
+
<%- if @sqlite3 -%>
|
14
|
+
task :release
|
15
|
+
<%- else -%>
|
13
16
|
task :release => 'db:migrate'
|
17
|
+
<%- end -%>
|
14
18
|
|
15
19
|
# SERVER step:
|
16
20
|
# - changes to the filesystem made here are deployed
|
17
21
|
# - full access to secrets, databases
|
18
22
|
# - failures here result in VM being stated, shutdown, and rolled back
|
19
23
|
# to last successful deploy (if any).
|
24
|
+
<%- if @sqlite3 -%>
|
25
|
+
task :server => [:swapfile, 'db:migrate'] do
|
26
|
+
<%- else -%>
|
20
27
|
task :server => :swapfile do
|
28
|
+
<%- end -%>
|
21
29
|
sh 'bin/rails server'
|
22
30
|
end
|
23
31
|
|
@@ -26,7 +34,11 @@ namespace :fly do
|
|
26
34
|
# - performance critical applications should scale memory to the
|
27
35
|
# point where swap is rarely used. 'fly scale help' for details.
|
28
36
|
# - disable by removing dependency on the :server task, thus:
|
37
|
+
<%- if @sqlite3 -%>
|
38
|
+
# task :server => 'db:migrate' do
|
39
|
+
<%- else -%>
|
29
40
|
# task :server do
|
41
|
+
<%- end -%>
|
30
42
|
task :swapfile do
|
31
43
|
sh 'fallocate -l 512M /swapfile'
|
32
44
|
sh 'chmod 0600 /swapfile'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
machine do
|
2
|
+
cpus 1
|
3
|
+
cpu_kind 'shared'
|
4
|
+
memory_mb 256
|
5
|
+
end
|
6
|
+
<% if @sqlite3 -%>
|
7
|
+
|
8
|
+
sqlite3 do
|
9
|
+
size 3
|
10
|
+
end
|
11
|
+
<% end -%>
|
12
|
+
<% if @postgresql -%>
|
13
|
+
|
14
|
+
postgres do
|
15
|
+
vm_size 'shared-cpu-1x'
|
16
|
+
volume_size 1
|
17
|
+
initial_cluster_size 1
|
18
|
+
end
|
19
|
+
<% end -%>
|
20
|
+
<% if @redis -%>
|
21
|
+
|
22
|
+
redis do
|
23
|
+
plan "Free"
|
24
|
+
end
|
25
|
+
<% end -%>
|
@@ -27,7 +27,9 @@ resource "fly_ip" "<%= @appName %>Ipv6" {
|
|
27
27
|
type = "v6"
|
28
28
|
}
|
29
29
|
|
30
|
+
<% unless @sqlite3 -%>
|
30
31
|
/* Uncomment this if you want a volume
|
32
|
+
<% end -%>
|
31
33
|
resource "fly_volume" "<%= @appName %>Volume" {
|
32
34
|
for_each = toset(<%= @regions.inspect %>)
|
33
35
|
region = each.value
|
@@ -36,7 +38,9 @@ resource "fly_volume" "<%= @appName %>Volume" {
|
|
36
38
|
app = <%= @app.inspect %>
|
37
39
|
size = 3
|
38
40
|
}
|
41
|
+
<% unless @sqlite3 -%>
|
39
42
|
*/
|
43
|
+
<% end -%>
|
40
44
|
|
41
45
|
# Start a fly machine
|
42
46
|
resource "fly_machine" "<%= @appName %>Machine" {
|
@@ -71,7 +75,7 @@ resource "fly_machine" "<%= @appName %>Machine" {
|
|
71
75
|
}
|
72
76
|
]
|
73
77
|
|
74
|
-
|
78
|
+
<%- if @sqlite3 -%>
|
75
79
|
env = {
|
76
80
|
DATABASE_URL = "sqlite3:///mnt/db/production.sqlite3"
|
77
81
|
}
|
@@ -83,6 +87,10 @@ resource "fly_machine" "<%= @appName %>Machine" {
|
|
83
87
|
}
|
84
88
|
]
|
85
89
|
|
90
|
+
depends_on = [fly_volume.<%= @appName %>Volume]
|
91
|
+
<%- else -%>
|
92
|
+
/* Uncomment this if you want a volume
|
86
93
|
depends_on = [fly_volume.<%= @appName %>Volume]
|
87
94
|
*/
|
95
|
+
<%- end -%>
|
88
96
|
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Restart Action Cable server on Redis connection failures.
|
2
|
+
# See: https://github.com/rails/rails/pull/45478
|
3
|
+
require 'action_cable/subscription_adapter/redis'
|
4
|
+
|
5
|
+
module ActionCableRedisListenerPatch
|
6
|
+
private
|
7
|
+
|
8
|
+
def ensure_listener_running
|
9
|
+
@thread ||= Thread.new do
|
10
|
+
Thread.current.abort_on_exception = true
|
11
|
+
conn = @adapter.redis_connection_for_subscriptions
|
12
|
+
listen conn
|
13
|
+
rescue ::Redis::BaseConnectionError
|
14
|
+
@thread = @raw_client = nil
|
15
|
+
::ActionCable.server.restart
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
ActionCable::SubscriptionAdapter::Redis::Listener.prepend(ActionCableRedisListenerPatch)
|
data/lib/tasks/fly.rake
CHANGED
@@ -12,25 +12,20 @@ namespace :fly do
|
|
12
12
|
if File.exist? 'fly.toml'
|
13
13
|
app = TOML.load_file('fly.toml')['app']
|
14
14
|
else
|
15
|
-
|
16
|
-
"flyctl apps create --generate-name --org personal --machines"
|
17
|
-
)
|
18
|
-
|
19
|
-
exit 1 unless output =~ /^New app created: /
|
20
|
-
|
21
|
-
@app = app = output.split.last
|
15
|
+
app = create_app
|
22
16
|
end
|
23
17
|
|
24
18
|
# ensure fly.toml and Dockerfile are present
|
25
19
|
action = Fly::Actions.new(app)
|
26
20
|
action.generate_toml if @app
|
21
|
+
action.generate_fly_config unless File.exist? 'config/fly.rb'
|
27
22
|
action.generate_dockerfile unless File.exist? 'Dockerfile'
|
28
23
|
action.generate_dockerignore unless File.exist? '.dockerignore'
|
29
24
|
action.generate_raketask unless File.exist? 'lib/tasks/fly.rake'
|
30
25
|
|
31
26
|
# build and push an image
|
32
27
|
out = FlyIoRails::Utils.tee 'fly deploy --build-only --push'
|
33
|
-
image = out[/image:\s+(.*)/, 1]
|
28
|
+
image = out[/image:\s+(.*)/, 1]&.strip
|
34
29
|
|
35
30
|
exit 1 unless image
|
36
31
|
|
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.2
|
5
5
|
platform: x64-mingw32
|
6
6
|
authors:
|
7
7
|
- Sam Ruby
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-09-
|
11
|
+
date: 2022-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fly-ruby
|
@@ -53,18 +53,23 @@ files:
|
|
53
53
|
- exe/x64-mingw32/wintun.dll
|
54
54
|
- lib/fly.io-rails.rb
|
55
55
|
- lib/fly.io-rails/actions.rb
|
56
|
+
- lib/fly.io-rails/dsl.rb
|
56
57
|
- lib/fly.io-rails/generators.rb
|
57
58
|
- lib/fly.io-rails/hcl.rb
|
58
59
|
- lib/fly.io-rails/machines.rb
|
59
60
|
- lib/fly.io-rails/platforms.rb
|
61
|
+
- lib/fly.io-rails/scanner.rb
|
60
62
|
- lib/fly.io-rails/utils.rb
|
61
63
|
- lib/fly.io-rails/version.rb
|
64
|
+
- lib/generators/fly/app_generator.rb
|
65
|
+
- lib/generators/fly/terraform_generator.rb
|
62
66
|
- lib/generators/templates/Dockerfile.erb
|
63
67
|
- lib/generators/templates/dockerignore.erb
|
64
68
|
- lib/generators/templates/fly.rake.erb
|
69
|
+
- lib/generators/templates/fly.rb.erb
|
65
70
|
- lib/generators/templates/fly.toml.erb
|
66
71
|
- lib/generators/templates/main.tf.erb
|
67
|
-
- lib/generators/
|
72
|
+
- lib/generators/templates/patches/action_cable.rb
|
68
73
|
- lib/tasks/fly.rake
|
69
74
|
homepage: https://github.com/rubys/fly-io.rails
|
70
75
|
licenses:
|
@@ -1,52 +0,0 @@
|
|
1
|
-
require 'open3'
|
2
|
-
require 'fly.io-rails/actions'
|
3
|
-
|
4
|
-
class Fly::Generators::TerraformGenerator < Rails::Generators::Base
|
5
|
-
include FlyIoRails::Utils
|
6
|
-
|
7
|
-
class_option :name, type: :string, required: false
|
8
|
-
class_option :org, type: :string, default: 'personal'
|
9
|
-
class_option :region, type: :array, repeatable: true, default: []
|
10
|
-
|
11
|
-
def terraform
|
12
|
-
source_paths.push File.expand_path('./templates', __dir__)
|
13
|
-
|
14
|
-
cmd = if options[:name]
|
15
|
-
"flyctl apps create #{options[:name].inspect} --org #{options[:org].inspect}"
|
16
|
-
else
|
17
|
-
"flyctl apps create --generate-name --org #{options[:org].inspect}"
|
18
|
-
end
|
19
|
-
|
20
|
-
output = tee cmd
|
21
|
-
exit 1 unless output =~ /^New app created: /
|
22
|
-
|
23
|
-
@app = output.split.last
|
24
|
-
template 'fly.toml.erb', 'fly.toml'
|
25
|
-
|
26
|
-
if options[:region].empty?
|
27
|
-
@regions = JSON.parse(`flyctl regions list --json`)['Regions'].
|
28
|
-
map {|region| region['Code']}
|
29
|
-
else
|
30
|
-
@regions = options[:regions].flatten
|
31
|
-
end
|
32
|
-
|
33
|
-
action = Fly::Actions.new(@app)
|
34
|
-
action.generate_all
|
35
|
-
|
36
|
-
credentials = nil
|
37
|
-
if File.exist? 'config/credentials/production.key'
|
38
|
-
credentials = 'config/credentials/production.key'
|
39
|
-
elsif File.exist? 'config/master.key'
|
40
|
-
credentials = 'config/master.key'
|
41
|
-
end
|
42
|
-
|
43
|
-
if credentials
|
44
|
-
say_status :run, "flyctl secrets set RAILS_MASTER_KEY from #{credentials}"
|
45
|
-
system "flyctl secrets set RAILS_MASTER_KEY=#{IO.read(credentials).chomp}"
|
46
|
-
puts
|
47
|
-
end
|
48
|
-
|
49
|
-
ENV['FLY_API_TOKEN'] = `flyctl auth token`
|
50
|
-
tee 'terraform init'
|
51
|
-
end
|
52
|
-
end
|