dockerfile-rails 1.3.0 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8780a8a7015d339452edf4dff330981a748ad72c72081e0cfa473c835121637c
4
- data.tar.gz: ab37e68fe9d3f82f9368e45738a12f171eaaabcff8b311451d89bdbce1217d95
3
+ metadata.gz: 70cab322c4f52cdddb9934de220be781a87ef306af7a21bf1db8a15c8db16a3a
4
+ data.tar.gz: a8363fcbdd774e3a2b3254f274366c0ad0f700eabdc90502a189c727005de25e
5
5
  SHA512:
6
- metadata.gz: 8a1bce2d42a5916ae7bf4cc49f23d0b9077533e1447015f35bfd4835397438a8bb4bc13e48a774a7e80130cf0f99dada597fda1fa0cada1674e52073d7846fc1
7
- data.tar.gz: 9793fdc06ffe7a84a1e0f40282570ea5e4ad6e08514ff8210f805254a093208cc4b98f683c44fce299dc0b71ff04c2418b847c5e33f05da407cefd617412521b
6
+ metadata.gz: 10fe62c57fe8f2088bbe0b58feafc4199e2591cf214d39e410d1e78be3f5039187ee28ed9279f774eb02ec991bd46318a014fa324bfc57fe16c9398c51844e10
7
+ data.tar.gz: 9bcc4bca316a3c4eb96a00060e1f3cbc45072975a28c16248353947da1e9259121fdbf7213e3a382cd407f48a1eb0d1b3baec65a9461242f1269f376a887d291
data/README.md CHANGED
@@ -52,6 +52,7 @@ Generally the dockerfile generator will be able to determine what dependencies y
52
52
  are actually using. But should you be using DATABASE_URL, for example, at runtime
53
53
  additional support may be needed:
54
54
 
55
+ * `--litefs` - use [LiteFS](https://fly.io/docs/litefs/)
55
56
  * `--mysql` - add mysql libraries
56
57
  * `--postgresql` - add postgresql libraries
57
58
  * `--redis` - add redis libraries
@@ -110,9 +111,9 @@ If you are running a single test, the following environment variables settings m
110
111
  * `TEST_CAPTURE=1` will capture test results.
111
112
  * `TEST_KEEP=1` will leave the test app behind for inspection after the test completes.
112
113
 
113
- ## Links
114
+ ## Historical Links
114
115
 
115
- The following links relate to the current development status with respect to Rails 7.1 and will be removed once that is resolved.
116
+ The following links relate to the coordination between this package and Rails 7.1.
116
117
 
117
118
  * [Preparations for Rails 7.1](https://community.fly.io/t/preparations-for-rails-7-1/9512) - [Fly.io](https://fly.io/)'s plans and initial discussions with DHH
118
119
  * [Rails Dockerfile futures](https://discuss.rubyonrails.org/t/rails-dockerfile-futures/82091/1) - rationale for a generator
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "erb"
4
+ require "json"
4
5
  require_relative "../dockerfile-rails/scanner.rb"
5
6
 
6
7
  class DockerfileGenerator < Rails::Generators::Base
@@ -15,6 +16,7 @@ class DockerfileGenerator < Rails::Generators::Base
15
16
  "jemalloc" => false,
16
17
  "label" => {},
17
18
  "link" => true,
19
+ "litefs" => false,
18
20
  "lock" => true,
19
21
  "max-idle" => nil,
20
22
  "mysql" => false,
@@ -111,6 +113,9 @@ class DockerfileGenerator < Rails::Generators::Base
111
113
  class_option :sqlite3, aliases: "--sqlite", type: :boolean, default: OPTION_DEFAULTS.sqlite3,
112
114
  desc: "include sqlite3 libraries"
113
115
 
116
+ class_option :litefs, type: :boolean, default: OPTION_DEFAULTS.litefs,
117
+ desc: "replicate sqlite3 databases using litefs"
118
+
114
119
  class_option :postgresql, aliases: "--postgres", type: :boolean, default: OPTION_DEFAULTS.postgresql,
115
120
  desc: "include postgresql libraries"
116
121
 
@@ -233,6 +238,23 @@ class DockerfileGenerator < Rails::Generators::Base
233
238
 
234
239
  template "docker-compose.yml.erb", "docker-compose.yml" if options.compose
235
240
 
241
+ if using_litefs?
242
+ template "litefs.yml.erb", "config/litefs.yml"
243
+
244
+ fly_attach_consul
245
+ end
246
+
247
+ if fly_processes # therefore File.exist?('fly.toml')
248
+ if File.stat("fly.toml").size > 0
249
+ template "fly.toml.erb", "fly.toml"
250
+ else
251
+ toml = fly_make_processes(fly_processes)
252
+ if toml != IO.read("fly.toml")
253
+ File.write "fly.toml", toml
254
+ end
255
+ end
256
+ end
257
+
236
258
  if @gemfile.include?("vite_ruby")
237
259
  package = JSON.load_file("package.json")
238
260
  unless package.dig("scripts", "build")
@@ -296,6 +318,10 @@ private
296
318
  options.root?
297
319
  end
298
320
 
321
+ def using_litefs?
322
+ options.litefs?
323
+ end
324
+
299
325
  def using_node?
300
326
  return @using_node if @using_node != nil
301
327
  @using_node = File.exist? "package.json"
@@ -495,6 +521,9 @@ private
495
521
  packages << "default-mysql-client" if options.mysql? || @mysql
496
522
  packages << "libjemalloc2" if options.jemalloc? && !options.fullstaq?
497
523
 
524
+ # litefs
525
+ packages += ["ca-certificates", "fuse3", "sudo"] if options.litefs?
526
+
498
527
  # ActiveStorage preview support
499
528
  packages << "libvips" if @gemfile.include? "ruby-vips"
500
529
 
@@ -601,13 +630,21 @@ private
601
630
  def deploy_env
602
631
  env = {}
603
632
 
604
- env["PORT"] = "3001" if options.nginx? && !using_passenger?
633
+ env["PORT"] = "3001" if (options.nginx? && !using_passenger?) || using_litefs?
605
634
 
606
635
  if Rails::VERSION::MAJOR < 7 || Rails::VERSION::STRING.start_with?("7.0")
607
636
  env["RAILS_LOG_TO_STDOUT"] = "1"
608
637
  env["RAILS_SERVE_STATIC_FILES"] = "true" unless options.nginx?
609
638
  end
610
639
 
640
+ if deploy_database == "sqlite3"
641
+ if using_litefs?
642
+ env["DATABASE_URL"] = "sqlite3:///litefs/production.sqlite3"
643
+ else
644
+ env["DATABASE_URL"] = "sqlite3:///data/production.sqlite3"
645
+ end
646
+ end
647
+
611
648
  if options.yjit?
612
649
  env["RUBY_YJIT_ENABLE"] = "1"
613
650
  end
@@ -639,7 +676,7 @@ private
639
676
 
640
677
  env.merge! @@vars["base"] if @@vars["base"]
641
678
 
642
- env.map { |key, value| "#{key}=#{value.inspect}" }
679
+ env.map { |key, value| "#{key}=#{value.inspect}" }.sort
643
680
  end
644
681
 
645
682
  def base_args
@@ -716,9 +753,11 @@ private
716
753
  end
717
754
 
718
755
  def deploy_database
719
- if options.postgresql? || @postgresql
756
+ # note: as database can be overridden at runtime via DATABASE_URL,
757
+ # use presence of "pg" or "mysql2" in the bundle as evidence of intent.
758
+ if options.postgresql? || @postgresql || @gemfile.include?("pg")
720
759
  "postgresql"
721
- elsif options.mysql? || @mysql
760
+ elsif options.mysql? || @mysql || @gemfile.include?("mysql2")
722
761
  "mysql"
723
762
  else
724
763
  "sqlite3"
@@ -823,6 +862,21 @@ private
823
862
  end
824
863
  end
825
864
 
865
+ def fly_processes
866
+ return unless File.exist? "fly.toml"
867
+ return unless using_sidekiq?
868
+
869
+ if procfile.size > 1
870
+ list = { "app" => "foreman start --procfile=Procfile.prod" }
871
+ else
872
+ list = { "app" => procfile.values.first }
873
+ end
874
+
875
+ list["sidekiq"] = "bundle exec sidekiq"
876
+
877
+ list
878
+ end
879
+
826
880
  def more_docker_ignores
827
881
  more = ""
828
882
 
@@ -853,4 +907,54 @@ private
853
907
  rescue ArgumentError
854
908
  nil
855
909
  end
910
+
911
+ # if running on fly v2, make a best effort to attach consul
912
+ def fly_attach_consul
913
+ # certainly not fly unless there is a fly.toml
914
+ return unless File.exist? "fly.toml"
915
+
916
+ # Check fly.toml to guess if v1 or v2
917
+ toml = File.read("fly.toml")
918
+ return if toml.include?("enable_consul") # v1-ism
919
+ return unless toml.include?("primary_region") # v2
920
+
921
+ # see if flyctl is in the path
922
+ paths = ENV["PATH"].split(File::PATH_SEPARATOR)
923
+ cmds = %w(flyctl)
924
+ exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
925
+ flyctl = Enumerator.product(paths, cmds, exts).
926
+ map { |path, cmd, ext| File.join(path, "#{cmd}#{ext}") }.
927
+ find { |path| File.executable? path }
928
+ return unless flyctl
929
+
930
+ # see if secret is already set?
931
+ begin
932
+ secrets = JSON.parse(`#{flyctl} secrets list --json`)
933
+ return if secrets.any? { |secret| secret["Name"] == "FLY_CONSUL_URL" }
934
+ rescue
935
+ return # likely got an error like "Could not find App"
936
+ end
937
+
938
+ # attach consul
939
+ say_status :execute, "flyctl consul attach", :green
940
+ system "#{flyctl} consul attach"
941
+ end
942
+
943
+ def fly_make_processes(list)
944
+ toml = File.read("fly.toml")
945
+
946
+ if toml.include? "[processes]"
947
+ toml.sub!(/\[processes\].*?(\n\n|\n?\z)/m, "[processes]\n" +
948
+ list.map { |name, cmd| " #{name} = #{cmd.inspect}" }.join("\n") + '\1')
949
+ else
950
+ toml += "\n[processes]\n" +
951
+ list.map { |name, cmd| " #{name} = #{cmd.inspect}\n" }.join
952
+
953
+ app = list.has_key?("app") ? "app" : list.keys.first
954
+
955
+ toml.sub! "[http_service]\n", "\\0 processes = [#{app.inspect}]\n"
956
+ end
957
+
958
+ toml
959
+ end
856
960
  end
@@ -134,6 +134,12 @@ RUN SECRET_KEY_BASE<%= Rails::VERSION::MAJOR<7 || Rails::VERSION::STRING.start_w
134
134
  # Final stage for app image
135
135
  FROM base
136
136
 
137
+ <% end -%>
138
+ <% if using_litefs? -%>
139
+ # Install, configure litefs
140
+ COPY --from=flyio/litefs:0.4.0 /usr/local/bin/litefs /usr/local/bin/litefs
141
+ COPY<% if options.link? %> --link<% end %> config/litefs.yml /etc/litefs.yml
142
+
137
143
  <% end -%>
138
144
  <% unless deploy_args.empty? -%>
139
145
  # Deployment build arguments
@@ -166,7 +172,11 @@ COPY --from=client /rails/<%= api_client_dir %>/build /rails/public
166
172
  <% end -%>
167
173
 
168
174
  <% end -%>
169
- <% unless run_as_root? -%>
175
+ <% if run_as_root? -%>
176
+ <% if deploy_database == 'sqlite3' -%>
177
+ RUN mkdir /data
178
+ <% end -%>
179
+ <% else -%>
170
180
  # Run and own only the runtime files as a non-root user for security
171
181
  <% if options.compose? -%>
172
182
  ARG UID=1000 \
@@ -182,11 +192,23 @@ RUN useradd rails --create-home --shell /bin/bash && \
182
192
  <% if deploy_packages.include?("sudo") && options.sudo? -%>
183
193
  sed -i 's/env_reset/env_keep="*"/' /etc/sudoers && \
184
194
  <% end -%>
195
+ <% if deploy_database == 'sqlite3' -%>
196
+ mkdir /data<% if using_litefs? %> /litefs<% end %> && \
197
+ chown -R rails:rails <%= Dir[*%w(db log storage tmp)].join(" ") %> /data<% if using_litefs? %> /litefs<% end %>
198
+ <% else -%>
185
199
  chown -R rails:rails <%= Dir[*%w(db log storage tmp)].join(" ") %>
186
- <% unless options.swap? or using_passenger? -%>
200
+ <% end -%>
201
+ <% unless options.swap? or using_passenger? or using_litefs? -%>
187
202
  USER rails:rails
188
203
  <% end -%>
189
204
 
205
+ <% end -%>
206
+ <% if using_litefs? and !run_as_root? -%>
207
+ # Authorize rails user to launch litefs
208
+ COPY <<-"EOF" /etc/sudoers.d/rails
209
+ rails ALL=(root) /usr/local/bin/litefs
210
+ EOF
211
+
190
212
  <% end -%>
191
213
  <% unless deploy_env.empty? -%>
192
214
  # Deployment options
@@ -208,11 +230,16 @@ COPY <<-"EOF" /rails/Procfile.prod
208
230
  <% end -%>
209
231
  EOF
210
232
 
233
+ <% end -%>
211
234
  # Start the server by default, this can be overwritten at runtime
212
235
  EXPOSE 3000
236
+ <% if deploy_database == 'sqlite3' -%>
237
+ VOLUME /data
238
+ <% end -%>
239
+ <% unless fly_processes -%>
240
+ <% if procfile.size > 1 -%>
213
241
  CMD ["foreman", "start", "--procfile=Procfile.prod"]
214
242
  <% else -%>
215
- # Start the server by default, this can be overwritten at runtime
216
- EXPOSE 3000
217
243
  CMD <%= procfile.values.first.split(" ").inspect %>
218
244
  <% end -%>
245
+ <% end -%>
@@ -82,7 +82,7 @@ services:
82
82
 
83
83
  sidekiq:
84
84
  build: .
85
- command: bin/sidekiq
85
+ command: bunde exec sidekiq
86
86
  environment:
87
87
  - RAILS_MASTER_KEY=$RAILS_MASTER_KEY
88
88
  - REDIS_URL=redis://redis-db:6379
@@ -14,11 +14,21 @@ if [ $UID -eq 0 ]; then
14
14
  <%= @space %>echo 10 > /proc/sys/vm/swappiness
15
15
  <%= @space %>swapon /swapfile
16
16
  <%= @space %>echo 1 > /proc/sys/vm/overcommit_memory
17
+ <% if using_litefs? -%>
18
+
19
+ <%= @space %># mount litefs
20
+ <%= @space %>litefs mount &
21
+ <% end -%>
17
22
  <% unless run_as_root? or using_passenger? -%>
23
+
18
24
  exec su rails $0 $@
19
25
  fi
20
26
  <% end -%>
21
27
 
28
+ <% elsif using_litefs? -%>
29
+ # mount litefs
30
+ <% unless run_as_root? %>sudo -E <% end %>litefs mount &
31
+
22
32
  <% end -%>
23
33
  <% if options.prepare -%>
24
34
  <% if procfile.size > 1 -%>
@@ -26,7 +36,7 @@ fi
26
36
  if [ "${*}" == "foreman start --procfile=Procfile.prod" ]; then
27
37
  <% else -%>
28
38
  # If running the rails server then create or migrate existing database
29
- if [ "${*}" == <%= procfile.values.first.inspect %> ]; then
39
+ if [ "${*}" == <%= procfile.values.first.inspect %> <% if using_litefs? %>-a "$FLY_REGION" == "$PRIMARY_REGION" <%end%>]; then
30
40
  <% end -%>
31
41
  <% if options.precompile == "defer" -%>
32
42
  ./bin/rails assets:precompile
@@ -0,0 +1 @@
1
+ <%= fly_make_processes(fly_processes) -%>
@@ -0,0 +1,116 @@
1
+ # based on: https://github.com/superfly/litefs/blob/main/cmd/litefs/etc/litefs.yml
2
+
3
+ # The FUSE section handles settings on the FUSE file system. FUSE
4
+ # provides a layer for intercepting SQLite transactions on the
5
+ # primary node so they can be shipped to replica nodes transparently.
6
+ fuse:
7
+ # Required. This is the mount directory that applications will
8
+ # use to access their SQLite databases.
9
+ dir: "/litefs"
10
+
11
+ # Set this flag to true to allow non-root users to access mount.
12
+ # You must set the "user_allow_other" option in /etc/fuse.conf first.
13
+ allow-other: false
14
+
15
+ # The debug flag enables debug logging of all FUSE API calls.
16
+ # This will produce a lot of logging. Not for general use.
17
+ debug: false
18
+
19
+ # The data section specifies where internal LiteFS data is stored
20
+ # and how long to retain the transaction files.
21
+ #
22
+ # Transaction files are used to ship changes to replica nodes so
23
+ # they should persist long enough for replicas to retrieve them,
24
+ # even in the face of a short network interruption or a redeploy.
25
+ # Under high load, these files can grow large so it's not advised
26
+ # to extend retention too long.
27
+ data:
28
+ # Path to internal data storage.
29
+ dir: "/data"
30
+
31
+ # Duration to keep LTX files. Latest LTX file is always kept.
32
+ retention: "10m"
33
+
34
+ # Frequency with which to check for LTX files to delete.
35
+ retention-monitor-interval: "1m"
36
+
37
+ # If true, then LiteFS will not wait until the node becomes the
38
+ # primary or connects to the primary before starting the subprocess.
39
+ skip-sync: false
40
+
41
+ # If true, then LiteFS will not exit if there is a validation
42
+ # issue on startup. This can be useful for debugging issues as
43
+ # it avoids constantly restarting the node on ephemeral hosting.
44
+ exit-on-error: false
45
+
46
+ # This section defines settings for the LiteFS HTTP API server.
47
+ # This API server is how nodes communicate with each other.
48
+ http:
49
+ # Specifies the bind address of the HTTP API server.
50
+ addr: ":20202"
51
+
52
+ # This section defines settings for the option HTTP proxy.
53
+ # This proxy can handle primary forwarding & replica consistency
54
+ # for applications that use a single SQLite database.
55
+ proxy:
56
+ # Specifies the bind address of the proxy server.
57
+ addr: ":3000"
58
+
59
+ # The hostport of the target application.
60
+ target: "localhost:3001"
61
+
62
+ # The name of the database used for TXID tracking.
63
+ db: "production.sqlite3"
64
+
65
+ # If true, enables verbose logging of requests by the proxy.
66
+ debug: false
67
+
68
+ # List of paths that are ignored by the proxy. The asterisk is
69
+ # the only available wildcard. These requests are passed
70
+ # through to the target as-is.
71
+ passthrough: []
72
+
73
+ # The lease section defines how LiteFS creates a cluster and
74
+ # implements leader election. For dynamic clusters, use the
75
+ # "consul". This allows the primary to change automatically when
76
+ # the current primary goes down. For a simpler setup, use
77
+ # "static" which assigns a single node to be the primary and does
78
+ # not failover.
79
+ lease:
80
+ # Required. Must be either "consul" or "static".
81
+ type: "consul"
82
+
83
+ # Required. The URL for this node's LiteFS API.
84
+ # Should match HTTP port.
85
+ advertise-url: "http://${HOSTNAME}.vm.${FLY_APP_NAME}.internal:20202"
86
+
87
+ # Specifies whether the node can become the primary. If using
88
+ # "static" leasing, this should be set to true on the primary
89
+ # and false on the replicas.
90
+ candidate: ${FLY_REGION == PRIMARY_REGION}
91
+
92
+ # A Consul server provides leader election and ensures that the
93
+ # responsibility of the primary node can be moved in the event
94
+ # of a deployment or a failure.
95
+ consul:
96
+ # Required. The base URL of the Consul server.
97
+ url: "${FLY_CONSUL_URL}"
98
+
99
+ # Required. The key used for obtaining a lease by the primary.
100
+ # This must be unique for each cluster of LiteFS servers
101
+ key: "litefs/${FLY_APP_NAME}"
102
+
103
+ # Length of time before a lease expires. The primary will
104
+ # automatically renew the lease while it is alive, however,
105
+ # if it fails to renew in time then a new primary may be
106
+ # elected after the TTL. This only occurs for unexpected loss
107
+ # of the leader as normal operation will allow the leader to
108
+ # handoff the lease to another replica without downtime.
109
+ #
110
+ # Consul does not allow a TTL of less than 10 seconds.
111
+ ttl: "10s"
112
+
113
+ # Length of time after the lease expires before a candidate
114
+ # can become leader. This buffer is intended to prevent
115
+ # overlap in leadership due to clock skew or in-flight calls.
116
+ lock-delay: "1s"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dockerfile-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Ruby
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-18 00:00:00.000000000 Z
11
+ date: 2023-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -49,12 +49,14 @@ files:
49
49
  - lib/generators/templates/docker-entrypoint.erb
50
50
  - lib/generators/templates/dockerfile.yml.erb
51
51
  - lib/generators/templates/dockerignore.erb
52
+ - lib/generators/templates/fly.toml.erb
53
+ - lib/generators/templates/litefs.yml.erb
52
54
  - lib/generators/templates/node-version.erb
53
- homepage: https://github.com/rubys/dockerfile-rails
55
+ homepage: https://github.com/fly-apps/dockerfile-rails
54
56
  licenses:
55
57
  - MIT
56
58
  metadata:
57
- homepage_uri: https://github.com/rubys/dockerfile-rails
59
+ homepage_uri: https://github.com/fly-apps/dockerfile-rails
58
60
  post_install_message:
59
61
  rdoc_options: []
60
62
  require_paths: