From 02de62d2c80ede2716275406f96e4727fbebae10 Mon Sep 17 00:00:00 2001 From: Sam Ruby Date: Tue, 24 Sep 2024 15:16:06 -0400 Subject: [PATCH] Enable DATABASE_URL to control the placement of all of the databases See: https://discuss.rubyonrails.org/t/brainstorming-approaches-to-reconcile-rails-8s-default-multi-db-setup-with-database-url/86769 --- lib/generators/dockerfile_generator.rb | 29 ++++++ lib/generators/templates/database.yml.erb | 1 + test/base.rb | 10 +++ test/results/postgresql/Dockerfile | 4 +- test/results/postgresql/database.yml | 102 ++++++++++++++++++++++ test/test_postgres.rb | 3 +- 6 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 lib/generators/templates/database.yml.erb create mode 100644 test/results/postgresql/database.yml diff --git a/lib/generators/dockerfile_generator.rb b/lib/generators/dockerfile_generator.rb index 50249c6..5e0a924 100644 --- a/lib/generators/dockerfile_generator.rb +++ b/lib/generators/dockerfile_generator.rb @@ -331,6 +331,8 @@ def generate_app template "docker-compose.yml.erb", "docker-compose.yml" if options.compose + template "database.yml.erb", "config/database.yml" if fix_database_config + if using_litefs? template "litefs.yml.erb", "config/litefs.yml" @@ -1450,4 +1452,31 @@ def fly_make_toml toml end + + # if there are multiple production databases defined, allow them all to be + # configured via DATABASE_URL. + def fix_database_config + yaml = IO.read("config/database.yml") + + production = YAML.load(yaml, aliases: true)["production"] + return unless production.is_a?(Hash) && production.values.all?(Hash) + return if production.keys == [ "primary" ] + + section = yaml[/^(production:.*?)(^\S|\z)/m, 1] + + replacement = section.gsub(/( ).*?\n((\1\s+).*?\n)*/) do |subsection| + spaces = $3 + name = subsection[/\w+/] + + if /^ +url:/.match?(subsection) + subsection + elsif name == "primary" + subsection + spaces + %(url: <%= ENV["DATABASE_URL"] %>\n) + else + subsection + spaces + %(url: <%= URI.parse(ENV["DATABASE_URL"]).tap { |url| url.path += "_#{name}" } if ENV["DATABASE_URL"] %>\n) + end + end + + yaml.sub(section, replacement) + end end diff --git a/lib/generators/templates/database.yml.erb b/lib/generators/templates/database.yml.erb new file mode 100644 index 0000000..dc5c170 --- /dev/null +++ b/lib/generators/templates/database.yml.erb @@ -0,0 +1 @@ +<%= fix_database_config %> \ No newline at end of file diff --git a/test/base.rb b/test/base.rb index c23a11b..ef7718c 100644 --- a/test/base.rb +++ b/test/base.rb @@ -110,6 +110,16 @@ def check_entrypoint assert_equal expected, results end + def check_database_config + results = IO.read("config/database.yml") + + IO.write("#{@results}/database.yml", results) if @capture + + expected = IO.read("#{@results}/database.yml") + + assert_equal expected, results + end + def teardown return if ENV["TEST_KEEP"] Dir.chdir ".." diff --git a/test/results/postgresql/Dockerfile b/test/results/postgresql/Dockerfile index ad69854..9427aa0 100644 --- a/test/results/postgresql/Dockerfile +++ b/test/results/postgresql/Dockerfile @@ -65,5 +65,5 @@ USER rails:rails ENTRYPOINT ["/rails/bin/docker-entrypoint"] # Start the server by default, this can be overwritten at runtime -EXPOSE 3000 -CMD ["./bin/rails", "server"] +EXPOSE 80 +CMD ["bundle", "exec", "thrust", "./bin/rails", "server"] diff --git a/test/results/postgresql/database.yml b/test/results/postgresql/database.yml new file mode 100644 index 0000000..40d1fcd --- /dev/null +++ b/test/results/postgresql/database.yml @@ -0,0 +1,102 @@ +# PostgreSQL. Versions 9.3 and up are supported. +# +# Install the pg driver: +# gem install pg +# On macOS with Homebrew: +# gem install pg -- --with-pg-config=/usr/local/bin/pg_config +# On Windows: +# gem install pg +# Choose the win32 build. +# Install PostgreSQL and put its /bin directory on your path. +# +# Configure Using Gemfile +# gem "pg" +# +default: &default + adapter: postgresql + encoding: unicode + # For details on connection pooling, see Rails configuration guide + # https://guides.rubyonrails.org/configuring.html#database-pooling + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + + +development: + <<: *default + database: test_postgresql_development + + # The specified database role being used to connect to PostgreSQL. + # To create additional roles in PostgreSQL see `$ createuser --help`. + # When left blank, PostgreSQL will use the default role. This is + # the same name as the operating system user running Rails. + #username: test_postgresql + + # The password associated with the PostgreSQL role (username). + #password: + + # Connect on a TCP socket. Omitted by default since the client uses a + # domain socket that doesn't need configuration. Windows does not have + # domain sockets, so uncomment these lines. + #host: localhost + + # The TCP port the server listens on. Defaults to 5432. + # If your server runs on a different port number, change accordingly. + #port: 5432 + + # Schema search path. The server defaults to $user,public + #schema_search_path: myapp,sharedapp,public + + # Minimum log levels, in increasing order: + # debug5, debug4, debug3, debug2, debug1, + # log, notice, warning, error, fatal, and panic + # Defaults to warning. + #min_messages: notice + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: test_postgresql_test + +# As with config/credentials.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password or a full connection URL as an environment +# variable when you boot the app. For example: +# +# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" +# +# If the connection URL is provided in the special DATABASE_URL environment +# variable, Rails will automatically merge its configuration values on top of +# the values provided in this file. Alternatively, you can specify a connection +# URL environment variable explicitly: +# +# production: +# url: <%= ENV["MY_APP_DATABASE_URL"] %> +# +# Read https://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full overview on how database connection configuration can be specified. +# +production: + primary: &primary_production + <<: *default + database: test_postgresql_production + username: test_postgresql + password: <%= ENV["TEST_POSTGRESQL_DATABASE_PASSWORD"] %> + url: <%= ENV["DATABASE_URL"] %> + cache: + <<: *primary_production + database: test_postgresql_production_cache + migrations_paths: db/cache_migrate + url: <%= URI.parse(ENV["DATABASE_URL"]).tap { |url| url.path += "_cache" } if ENV["DATABASE_URL"] %> + queue: + <<: *primary_production + database: test_postgresql_production_queue + migrations_paths: db/queue_migrate + url: <%= URI.parse(ENV["DATABASE_URL"]).tap { |url| url.path += "_queue" } if ENV["DATABASE_URL"] %> + cable: + <<: *primary_production + database: test_postgresql_production_cable + migrations_paths: db/cable_migrate + url: <%= URI.parse(ENV["DATABASE_URL"]).tap { |url| url.path += "_cable" } if ENV["DATABASE_URL"] %> diff --git a/test/test_postgres.rb b/test/test_postgres.rb index c19ecf6..944530c 100644 --- a/test/test_postgres.rb +++ b/test/test_postgres.rb @@ -3,11 +3,12 @@ require_relative "base" class TestPostgresql < TestBase - @rails_options = "--database=postgresql" + @rails_options = "--database=postgresql --main" @generate_options = "--compose" def test_postgresql check_dockerfile check_compose + check_database_config end end