dockerfile-rails 1.3.0 → 1.4.0

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: 8780a8a7015d339452edf4dff330981a748ad72c72081e0cfa473c835121637c
4
- data.tar.gz: ab37e68fe9d3f82f9368e45738a12f171eaaabcff8b311451d89bdbce1217d95
3
+ metadata.gz: 5d1b0d5e237b0a8c33a3b1263d34590db8912967420863332ad0b2c8f5661e5f
4
+ data.tar.gz: 000fd8db20304bc2ba8623aaa1a4bd19870ec4143cacc3cb6ea31b8690510782
5
5
  SHA512:
6
- metadata.gz: 8a1bce2d42a5916ae7bf4cc49f23d0b9077533e1447015f35bfd4835397438a8bb4bc13e48a774a7e80130cf0f99dada597fda1fa0cada1674e52073d7846fc1
7
- data.tar.gz: 9793fdc06ffe7a84a1e0f40282570ea5e4ad6e08514ff8210f805254a093208cc4b98f683c44fce299dc0b71ff04c2418b847c5e33f05da407cefd617412521b
6
+ metadata.gz: 64b8702f00462a318af21238617528682be3831da82471bff0172ef9ef98d7e4a6a10346ffe297e87e76cc5d4b2699bf56b79d9257f1eee3c8cc24bc56012037
7
+ data.tar.gz: 9a9dc774d388e4276b08bbcdcce9ffd676800df7bb86b4cac34e6795fd0ee6f24ba2a57df393f7948f738a9bdafe8a1ed8073c0b91d9e6a2f9f94349a7d14fe4
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,12 @@ 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
+
236
247
  if @gemfile.include?("vite_ruby")
237
248
  package = JSON.load_file("package.json")
238
249
  unless package.dig("scripts", "build")
@@ -296,6 +307,10 @@ private
296
307
  options.root?
297
308
  end
298
309
 
310
+ def using_litefs?
311
+ options.litefs?
312
+ end
313
+
299
314
  def using_node?
300
315
  return @using_node if @using_node != nil
301
316
  @using_node = File.exist? "package.json"
@@ -495,6 +510,9 @@ private
495
510
  packages << "default-mysql-client" if options.mysql? || @mysql
496
511
  packages << "libjemalloc2" if options.jemalloc? && !options.fullstaq?
497
512
 
513
+ # litefs
514
+ packages += ["ca-certificates", "fuse3", "sudo"] if options.litefs?
515
+
498
516
  # ActiveStorage preview support
499
517
  packages << "libvips" if @gemfile.include? "ruby-vips"
500
518
 
@@ -601,13 +619,21 @@ private
601
619
  def deploy_env
602
620
  env = {}
603
621
 
604
- env["PORT"] = "3001" if options.nginx? && !using_passenger?
622
+ env["PORT"] = "3001" if (options.nginx? && !using_passenger?) || using_litefs?
605
623
 
606
624
  if Rails::VERSION::MAJOR < 7 || Rails::VERSION::STRING.start_with?("7.0")
607
625
  env["RAILS_LOG_TO_STDOUT"] = "1"
608
626
  env["RAILS_SERVE_STATIC_FILES"] = "true" unless options.nginx?
609
627
  end
610
628
 
629
+ if deploy_database == "sqlite3"
630
+ if using_litefs?
631
+ env["DATABASE_URL"] = "sqlite3:///litefs/production.sqlite3"
632
+ else
633
+ env["DATABASE_URL"] = "sqlite3:///data/production.sqlite3"
634
+ end
635
+ end
636
+
611
637
  if options.yjit?
612
638
  env["RUBY_YJIT_ENABLE"] = "1"
613
639
  end
@@ -639,7 +665,7 @@ private
639
665
 
640
666
  env.merge! @@vars["base"] if @@vars["base"]
641
667
 
642
- env.map { |key, value| "#{key}=#{value.inspect}" }
668
+ env.map { |key, value| "#{key}=#{value.inspect}" }.sort
643
669
  end
644
670
 
645
671
  def base_args
@@ -716,9 +742,11 @@ private
716
742
  end
717
743
 
718
744
  def deploy_database
719
- if options.postgresql? || @postgresql
745
+ # note: as database can be overridden at runtime via DATABASE_URL,
746
+ # use presence of "pg" or "mysql2" in the bundle as evidence of intent.
747
+ if options.postgresql? || @postgresql || @gemfile.include?("pg")
720
748
  "postgresql"
721
- elsif options.mysql? || @mysql
749
+ elsif options.mysql? || @mysql || @gemfile.include?("mysql2")
722
750
  "mysql"
723
751
  else
724
752
  "sqlite3"
@@ -853,4 +881,32 @@ private
853
881
  rescue ArgumentError
854
882
  nil
855
883
  end
884
+
885
+ # if running on fly v2, make a best effort to attach consul
886
+ def fly_attach_consul
887
+ # certainly not fly unless there is a fly.toml
888
+ return unless File.exist? "fly.toml"
889
+
890
+ # Check fly.toml to guess if v1 or v2
891
+ toml = File.read("fly.toml")
892
+ return if toml.include?("enable_consul") # v1-ism
893
+ return unless toml.include?("primary_region") # v2
894
+
895
+ # see if flyctl is in the path
896
+ paths = ENV["PATH"].split(File::PATH_SEPARATOR)
897
+ cmds = %w(flyctl)
898
+ exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
899
+ flyctl = Enumerator.product(paths, cmds, exts).
900
+ map { |path, cmd, ext| File.join(path, "#{cmd}#{ext}") }.
901
+ find { |path| File.executable? path }
902
+ return unless flyctl
903
+
904
+ # see if secret is already set?
905
+ secrets = JSON.parse(`#{flyctl} secrets list --json`)
906
+ return if secrets.any? { |secret| secret["Name"] == "FLY_CONSUL_URL" }
907
+
908
+ # attach consul
909
+ say_status "execute", "flyctl consul attach", :green
910
+ system "#{flyctl} consul attach"
911
+ end
856
912
  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
@@ -210,9 +232,15 @@ EOF
210
232
 
211
233
  # Start the server by default, this can be overwritten at runtime
212
234
  EXPOSE 3000
235
+ <% if deploy_database == 'sqlite3' -%>
236
+ VOLUME /data
237
+ <% end -%>
213
238
  CMD ["foreman", "start", "--procfile=Procfile.prod"]
214
239
  <% else -%>
215
240
  # Start the server by default, this can be overwritten at runtime
216
241
  EXPOSE 3000
242
+ <% if deploy_database == 'sqlite3' -%>
243
+ VOLUME /data
244
+ <% end -%>
217
245
  CMD <%= procfile.values.first.split(" ").inspect %>
218
246
  <% end -%>
@@ -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,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.0
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-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -49,12 +49,13 @@ 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/litefs.yml.erb
52
53
  - lib/generators/templates/node-version.erb
53
- homepage: https://github.com/rubys/dockerfile-rails
54
+ homepage: https://github.com/fly-apps/dockerfile-rails
54
55
  licenses:
55
56
  - MIT
56
57
  metadata:
57
- homepage_uri: https://github.com/rubys/dockerfile-rails
58
+ homepage_uri: https://github.com/fly-apps/dockerfile-rails
58
59
  post_install_message:
59
60
  rdoc_options: []
60
61
  require_paths: