fly.io-rails 0.0.21-arm64-darwin → 0.1.1-arm64-darwin

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f173785bd8b4b709b0c35c2c339d576d42ad8874abf0e37237f12b0fa8dd153d
4
- data.tar.gz: 924210805c7a4023087882c1645411bb61608f420ae8ade16941e637148aa0c2
3
+ metadata.gz: 1fd535a7501f08c7e1314a14b6a2875b334182fca732dd994df20ac2979e0bf9
4
+ data.tar.gz: e9667f63230a373fd8feb431ca7a551e74a6b17a118418f2f13f37da80086964
5
5
  SHA512:
6
- metadata.gz: 4dc311c231af69a08ed89182f7593501576c07b15f7e098250b103108a3f2909f0563fc16b3fe77803937ef504f0eee93854d7597505f6e4c1c0276183e88349
7
- data.tar.gz: b56078f2ba98e8a8486043b518f2da121989590f9fd8672c2c216e32f4a2ad0c43689f8ff9216626f39be29501000f049bb5c2c1cc1bdb2681346dd9bb9dacdd
6
+ metadata.gz: dafa2169129603c071bcf199f215074e66ae166c4fd7cf78feb081276d14f1460c3fb3f81186a0aa1cf0708fdc47ca5e62dd0ce4fe742002369abfe4eb043f34
7
+ data.tar.gz: 75d78bab12736653419348eca68f94fdcd4bd20532e6310ade12fc96f8a78065d973ebfcf7fd276972da28b44701251c31421421928c64a5d716fce70521301c
Binary file
@@ -2,11 +2,13 @@ require 'thor'
2
2
  require 'active_support'
3
3
  require 'active_support/core_ext/string/inflections'
4
4
  require 'fly.io-rails/machines'
5
+ require 'fly.io-rails/utils'
5
6
 
6
7
  module Fly
7
8
  class Actions < Thor::Group
8
9
  include Thor::Actions
9
10
  include Thor::Base
11
+ include Thor::Shell
10
12
  attr_accessor :options
11
13
 
12
14
  def initialize(app = nil)
@@ -36,6 +38,11 @@ module Fly
36
38
 
37
39
  source_paths.push File::expand_path('../generators/templates', __dir__)
38
40
 
41
+ def generate_toml
42
+ app
43
+ template 'fly.toml.erb', 'fly.toml'
44
+ end
45
+
39
46
  def generate_dockerfile
40
47
  app
41
48
  template 'Dockerfile.erb', 'Dockerfile'
@@ -62,5 +69,282 @@ module Fly
62
69
  generate_terraform
63
70
  generate_raketask
64
71
  end
72
+
73
+ def generate_ipv4
74
+ cmd = 'flyctl ips allocate-v4'
75
+ say_status :run, cmd
76
+ system cmd
77
+ end
78
+
79
+ def generate_ipv6
80
+ cmd = 'flyctl ips allocate-v6'
81
+ say_status :run, cmd
82
+ system cmd
83
+ end
84
+
85
+ def create_volume(app, region, size)
86
+ volume = "#{app.gsub('-', '_')}_volume"
87
+ volumes = JSON.parse(`flyctl volumes list --json`).
88
+ map {|volume| volume['Name']}
89
+
90
+ unless volumes.include? volume
91
+ cmd = "flyctl volumes create #{volume} --app #{app} --region #{region} --size #{size}"
92
+ say_status :run, cmd
93
+ system cmd
94
+ end
95
+
96
+ volume
97
+ end
98
+
99
+ def create_postgres(app, org, region, vm_size, volume_size, cluster_size)
100
+ 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
+ say_status :run, cmd
102
+ output = FlyIoRails::Utils.tee(cmd)
103
+ output[%r{postgres://\S+}]
104
+ end
105
+
106
+ def create_redis(app, org, region, eviction)
107
+ # see if redis is already defined
108
+ name = `flyctl redis list`.lines[1..-2].map(&:split).
109
+ find {|tokens| tokens[1] == org}&.first
110
+
111
+ if name
112
+ secret = `flyctl redis status #{name}`[%r{redis://\S+}]
113
+ return secret if secret
114
+ end
115
+
116
+ # create a new redis
117
+ cmd = "flyctl redis create --org #{org} --name #{app}-redis --region #{region} --no-replicas #{eviction} --plan Free"
118
+ say_status :run, cmd
119
+ output = FlyIoRails::Utils.tee(cmd)
120
+ output[%r{redis://\S+}]
121
+ end
122
+
123
+ def release(app, config)
124
+ start = Fly::Machines.create_and_start_machine(app, config: config)
125
+ machine = start[:id]
126
+
127
+ if !machine
128
+ STDERR.puts 'Error starting release machine'
129
+ PP.pp start, STDERR
130
+ exit 1
131
+ end
132
+
133
+ status = Fly::Machines.wait_for_machine app, machine,
134
+ timeout: 60, state: 'started'
135
+
136
+ # wait for release to copmlete
137
+ status = nil
138
+ 5.times do
139
+ status = Fly::Machines.wait_for_machine app, machine,
140
+ timeout: 60, state: 'stopped'
141
+ return machine if status[:ok]
142
+ end
143
+
144
+ # wait for release to copmlete
145
+ event = nil
146
+ 90.times do
147
+ sleep 1
148
+ status = Fly::Machines.get_a_machine app, machine
149
+ event = status[:events]&.first
150
+ return machine if event && event[:type] == 'exit'
151
+ end
152
+
153
+ STDERR.puts event.to_json
154
+ exit 1
155
+ end
156
+
157
+ def deploy(app, image)
158
+ regions = JSON.parse(`flyctl regions list --json`)['Regions'].
159
+ map {|region| region['Code']} rescue []
160
+ region = regions.first || 'iad'
161
+
162
+ secrets = JSON.parse(`flyctl secrets list --json`).
163
+ map {|secret| secret["Name"]}
164
+
165
+ config = {
166
+ region: region,
167
+ app: app,
168
+ name: "#{app}-machine",
169
+ image: image,
170
+ guest: {
171
+ cpus: 1,
172
+ cpu_kind: "shared",
173
+ memory_mb: 256,
174
+ },
175
+ services: [
176
+ {
177
+ ports: [
178
+ {port: 443, handlers: ["tls", "http"]},
179
+ {port: 80, handlers: ["http"]}
180
+ ],
181
+ protocol: "tcp",
182
+ internal_port: 8080
183
+ }
184
+ ]
185
+ }
186
+
187
+ database = YAML.load_file('config/database.yml').
188
+ dig('production', 'adapter') rescue nil
189
+ cable = YAML.load_file('config/cable.yml').
190
+ dig('production', 'adapter') rescue nil
191
+
192
+ if database == 'sqlite3'
193
+ volume = create_volume(app, region, 3)
194
+
195
+ config[:mounts] = [
196
+ { volume: volume, path: '/mnt/volume' }
197
+ ]
198
+
199
+ config[:env] = {
200
+ "DATABASE_URL" => "sqlite3:///mnt/volume/production.sqlite3"
201
+ }
202
+ elsif database == 'postgresql' and not secrets.include? 'DATABASE_URL'
203
+ secret = create_postgres(app, @org, region, 'shared-cpu-1x', 1, 1)
204
+
205
+ if secret
206
+ cmd = "flyctl secrets set --stage DATABASE_URL=#{secret}"
207
+ say_status :run, cmd
208
+ system cmd
209
+ end
210
+ end
211
+
212
+ # Enable redis if mentioned as a cache provider or a cable provider.
213
+ # Set eviction policy to true if a cache provider, else false.
214
+ eviction = nil
215
+
216
+ if (YAML.load_file('config/cable.yml').dig('production', 'adapter') rescue false)
217
+ eviction = '--disable-eviction'
218
+ end
219
+
220
+ if (IO.read('config/environments/production.rb') =~ /redis/i rescue false)
221
+ eviction = '--enable-eviction'
222
+ end
223
+
224
+ if eviction and not secrets.include? 'REDIS_URL'
225
+ secret = create_redis(app, @org, region, eviction)
226
+
227
+ if secret
228
+ cmd = "flyctl secrets set --stage REDIS_URL=#{secret}"
229
+ say_status :run, cmd
230
+ system cmd
231
+ end
232
+ end
233
+
234
+ # build config for release machine, overriding server command
235
+ release_config = config.dup
236
+ release_config.delete :services
237
+ release_config.delete :mounts
238
+ release_config[:env] = { 'SERVER_COMMAND' => 'bin/rails fly:release' }
239
+
240
+ # perform release
241
+ say_status :fly, release_config[:env]['SERVER_COMMAND']
242
+ machine = release(app, release_config)
243
+ Fly::Machines.delete_machine app, machine if machine
244
+
245
+ # start proxy, if necessary
246
+ endpoint = Fly::Machines::fly_api_hostname!
247
+
248
+ # stop previous instances
249
+ JSON.parse(`fly machines list --json`).each do |list|
250
+ next if list['id'] == machine
251
+ system "fly machines remove --force #{list['id']}"
252
+ end
253
+
254
+ # start app
255
+ say_status :fly, "start #{app}"
256
+ start = Fly::Machines.create_and_start_machine(app, config: config)
257
+ machine = start[:id]
258
+
259
+ if !machine
260
+ STDERR.puts 'Error starting application'
261
+ PP.pp start, STDERR
262
+ exit 1
263
+ end
264
+
265
+ 5.times do
266
+ status = Fly::Machines.wait_for_machine app, machine,
267
+ timeout: 60, status: 'started'
268
+ return if status[:ok]
269
+ end
270
+
271
+ STDERR.puts 'Timeout waiting for application to start'
272
+ end
273
+
274
+ def terraform(app, image)
275
+ # update main.tf with the image name
276
+ tf = IO.read('main.tf')
277
+ tf[/^\s*image\s*=\s*"(.*?)"/, 1] = image.strip
278
+ IO.write 'main.tf', tf
279
+
280
+ # find first machine in terraform config file
281
+ machines = Fly::HCL.parse(IO.read('main.tf')).find {|block|
282
+ block.keys.first == :resource and
283
+ block.values.first.keys.first == 'fly_machine'}
284
+
285
+ # extract HCL configuration for the machine
286
+ config = machines.values.first.values.first.values.first
287
+
288
+ # delete HCL specific configuration items
289
+ %i(services for_each region app name depends_on).each do |key|
290
+ config.delete key
291
+ end
292
+
293
+ # move machine configuration into guest object
294
+ config[:guest] = {
295
+ cpus: config.delete(:cpus),
296
+ memory_mb: config.delete(:memorymb),
297
+ cpu_kind: config.delete(:cputype)
298
+ }
299
+
300
+ # release machines should have no services or mounts
301
+ config.delete :services
302
+ config.delete :mounts
303
+
304
+ # override start command
305
+ config[:env] ||= {}
306
+ config[:env]['SERVER_COMMAND'] = 'bin/rails fly:release'
307
+
308
+ # start proxy, if necessary
309
+ endpoint = Fly::Machines::fly_api_hostname!
310
+
311
+ # start release machine
312
+ STDERR.puts "--> #{config[:env]['SERVER_COMMAND']}"
313
+ start = Fly::Machines.create_and_start_machine(app, config: config)
314
+ machine = start[:id]
315
+
316
+ if !machine
317
+ STDERR.puts 'Error starting release machine'
318
+ PP.pp start, STDERR
319
+ exit 1
320
+ end
321
+
322
+ # wait for release to copmlete
323
+ event = nil
324
+ 90.times do
325
+ sleep 1
326
+ status = Fly::Machines.get_a_machine app, machine
327
+ event = status[:events]&.first
328
+ break if event && event[:type] == 'exit'
329
+ end
330
+
331
+ # extract exit code
332
+ exit_code = event.dig(:request, :exit_event, :exit_code)
333
+
334
+ if exit_code == 0
335
+ # delete release machine
336
+ Fly::Machines.delete_machine app, machine
337
+
338
+ # use terraform apply to deploy
339
+ ENV['FLY_API_TOKEN'] = `flyctl auth token`.chomp
340
+ ENV['FLY_HTTP_ENDPOINT'] = endpoint if endpoint
341
+ system 'terraform apply -auto-approve'
342
+ else
343
+ STDERR.puts 'Error performing release'
344
+ STDERR.puts (exit_code ? {exit_code: exit_code} : event).inspect
345
+ STDERR.puts "run 'flyctl logs --instance #{machine}' for more information"
346
+ exit 1
347
+ end
348
+ end
65
349
  end
66
350
  end
@@ -80,7 +80,7 @@ module Fly
80
80
  get "/v1/apps/#{app}"
81
81
  end
82
82
 
83
- # create_start_machine 'user-functions', name: 'quirky_machine', config: {
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,13 +94,13 @@ module Fly
94
94
  # }
95
95
  # ]
96
96
  # }
97
- def self.create_start_machine app, options
97
+ def self.create_and_start_machine app, options
98
98
  post "/v1/apps/#{app}/machines", options
99
99
  end
100
100
 
101
- # wait_for_machine_to_start 'user-functions', '73d8d46dbee589'
102
- def self.wait_for_machine_to_start app, machine, timeout=60
103
- get "/v1/apps/#{app}/machines/#{machine}/wait?timeout=#{timeout}"
101
+ # wait_for_machine 'user-functions', '73d8d46dbee589'
102
+ def self.wait_for_machine app, machine, options = {timeout:60, status: 'started'}
103
+ get "/v1/apps/#{app}/machines/#{machine}/wait?#{options.to_query}"
104
104
  end
105
105
 
106
106
  # get_a_machine machine 'user-functions', '73d8d46dbee589'
@@ -1,3 +1,3 @@
1
1
  module Fly_io
2
- VERSION = '0.0.21'
2
+ VERSION = '0.1.1'
3
3
  end
@@ -1,7 +1,7 @@
1
1
  require 'open3'
2
2
  require 'fly.io-rails/actions'
3
3
 
4
- class TerraformGenerator < Rails::Generators::Base
4
+ class Fly::Generators::TerraformGenerator < Rails::Generators::Base
5
5
  include FlyIoRails::Utils
6
6
 
7
7
  class_option :name, type: :string, required: false
data/lib/tasks/fly.rake CHANGED
@@ -1,90 +1,45 @@
1
1
  require 'fly.io-rails/machines'
2
2
  require 'fly.io-rails/hcl'
3
+ require 'fly.io-rails/actions'
4
+ require 'toml'
3
5
 
4
6
  namespace :fly do
5
7
  desc 'Deploy fly application'
6
8
  task :deploy do
7
- # build and push an image
8
- out = FlyIoRails::Utils.tee 'fly deploy --build-only --push'
9
- image = out[/image:\s+(.*)/, 1]
10
-
11
- exit 1 unless image
12
-
13
- # update main.tf with the image name
14
- tf = IO.read('main.tf')
15
- tf[/^\s*image\s*=\s*"(.*?)"/, 1] = image.strip
16
- IO.write 'main.tf', tf
9
+ include FlyIoRails::Utils
17
10
 
18
- # find first machine in terraform config file
19
- machines = Fly::HCL.parse(IO.read('main.tf')).find {|block|
20
- block.keys.first == :resource and
21
- block.values.first.keys.first == 'fly_machine'}
22
-
23
- # extract HCL configuration for the machine
24
- config = machines.values.first.values.first.values.first
11
+ # Get app name, creating one if necessary
12
+ if File.exist? 'fly.toml'
13
+ app = TOML.load_file('fly.toml')['app']
14
+ else
15
+ output = FlyIoRails::Utils.tee(
16
+ "flyctl apps create --generate-name --org personal --machines"
17
+ )
25
18
 
26
- # extract fly application name
27
- app = config[:app]
19
+ exit 1 unless output =~ /^New app created: /
28
20
 
29
- # delete HCL specific configuration items
30
- %i(services for_each region app name depends_on).each do |key|
31
- config.delete key
21
+ @app = app = output.split.last
32
22
  end
33
23
 
34
- # move machine configuration into guest object
35
- config[:guest] = {
36
- cpus: config.delete(:cpus),
37
- memory_mb: config.delete(:memorymb),
38
- cpu_kind: config.delete(:cputype)
39
- }
40
-
41
- # release machines should have no services or mounts
42
- config.delete :services
43
- config.delete :mounts
44
-
45
- # override start command
46
- config[:env] ||= {}
47
- config[:env]['SERVER_COMMAND'] = 'bin/rails fly:release'
24
+ # ensure fly.toml and Dockerfile are present
25
+ action = Fly::Actions.new(app)
26
+ action.generate_toml if @app
27
+ action.generate_dockerfile unless File.exist? 'Dockerfile'
28
+ action.generate_dockerignore unless File.exist? '.dockerignore'
29
+ action.generate_raketask unless File.exist? 'lib/tasks/fly.rake'
48
30
 
49
- # start proxy, if necessary
50
- endpoint = Fly::Machines::fly_api_hostname!
51
-
52
- # start release machine
53
- STDERR.puts "--> #{config[:env]['SERVER_COMMAND']}"
54
- start = Fly::Machines.create_start_machine(app, config: config)
55
- machine = start[:id]
56
-
57
- if !machine
58
- STDERR.puts 'Error starting release machine'
59
- PP.pp start, STDERR
60
- exit 1
61
- end
62
-
63
- # wait for release to copmlete
64
- event = nil
65
- 90.times do
66
- sleep 1
67
- status = Fly::Machines.get_a_machine app, machine
68
- event = status[:events]&.first
69
- break if event && event[:type] == 'exit'
70
- end
31
+ # build and push an image
32
+ out = FlyIoRails::Utils.tee 'fly deploy --build-only --push'
33
+ image = out[/image:\s+(.*)/, 1].strip
71
34
 
72
- # extract exit code
73
- exit_code = event.dig(:request, :exit_event, :exit_code)
74
-
75
- if exit_code == 0
76
- # delete release machine
77
- Fly::Machines.delete_machine app, machine
35
+ exit 1 unless image
78
36
 
79
- # use terraform apply to deploy
80
- ENV['FLY_API_TOKEN'] = `flyctl auth token`.chomp
81
- ENV['FLY_HTTP_ENDPOINT'] = endpoint if endpoint
82
- system 'terraform apply -auto-approve'
37
+ if File.exist? 'main.tf'
38
+ action.terraform(app, image)
83
39
  else
84
- STDERR.puts 'Error performing release'
85
- STDERR.puts (exit_code ? {exit_code: exit_code} : event).inspect
86
- STDERR.puts "run 'flyctl logs --instance #{machine}' for more information"
87
- exit 1
40
+ action.generate_ipv4 if @app
41
+ action.generate_ipv6 if @app
42
+ action.deploy(app, image)
88
43
  end
89
44
  end
90
45
  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.0.21
4
+ version: 0.1.1
5
5
  platform: arm64-darwin
6
6
  authors:
7
7
  - Sam Ruby
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-23 00:00:00.000000000 Z
11
+ date: 2022-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fly-ruby