From 2ec397ff9fc2b384591d27878459f339c34e5485 Mon Sep 17 00:00:00 2001 From: talyz Date: Tue, 16 Feb 2021 12:08:59 +0100 Subject: [PATCH 1/7] nixos/gitlab: Clean up the config dir more thoroughly This removes all the subdirectories in `config` on start. From one version of GitLab to the next, the files in the `config` directory changes. Since we're only overwriting the existing files with ones from the repo, cruft sometimes gets left behind, occationally causing issues. Ideally, all configuration put in the `config` directory is declared by NixOS options and we could just remove the whole directory on start, but I'm not sure if that's the case. It would also require a little bit of additional rework and testing. The subdirectories, however, should seldom contain user configuration and the ones that frequently does, `initializers`, is already removed on start. --- nixos/modules/services/misc/gitlab.nix | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 61faeab7d32..4086a11ce87 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -703,7 +703,6 @@ in { "d ${cfg.statePath} 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/builds 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/config 0750 ${cfg.user} ${cfg.group} -" - "d ${cfg.statePath}/config/initializers 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/db 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/log 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.statePath}/repositories 2770 ${cfg.user} ${cfg.group} -" @@ -879,10 +878,12 @@ in { preStart = '' set -eu + umask u=rwx,g=rx,o= + cp -f ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION rm -rf ${cfg.statePath}/db/* - rm -rf ${cfg.statePath}/config/initializers/* rm -f ${cfg.statePath}/lib + find '${cfg.statePath}/config/' -maxdepth 1 -mindepth 1 -type d -execdir rm -rf {} \; cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db ln -sf ${extraGitlabRb} ${cfg.statePath}/config/initializers/extra-gitlab.rb @@ -929,9 +930,7 @@ in { "${cfg.statePath}/config/gitlab.yml" } - if [[ -h '${cfg.statePath}/config/secrets.yml' ]]; then - rm '${cfg.statePath}/config/secrets.yml' - fi + rm -f '${cfg.statePath}/config/secrets.yml' export secret="$(<'${cfg.secrets.secretFile}')" export db="$(<'${cfg.secrets.dbFile}')" From f8ab43ef7b14ae9e4bb17e058849af0d9eaf759d Mon Sep 17 00:00:00 2001 From: talyz Date: Wed, 17 Feb 2021 16:36:58 +0100 Subject: [PATCH 2/7] nixos/gitlab: Switch from unicorn to puma Puma is the new upstream default server since GitLab 13. --- .../services/misc/defaultUnicornConfig.rb | 69 ---------- nixos/modules/services/misc/gitlab.nix | 10 +- .../version-management/gitlab/default.nix | 1 + .../gitlab/remove-hardcoded-locations.patch | 124 ++++++++++++------ 4 files changed, 91 insertions(+), 113 deletions(-) delete mode 100644 nixos/modules/services/misc/defaultUnicornConfig.rb diff --git a/nixos/modules/services/misc/defaultUnicornConfig.rb b/nixos/modules/services/misc/defaultUnicornConfig.rb deleted file mode 100644 index 0b58c59c7a5..00000000000 --- a/nixos/modules/services/misc/defaultUnicornConfig.rb +++ /dev/null @@ -1,69 +0,0 @@ -worker_processes 3 - -listen ENV["UNICORN_PATH"] + "/tmp/sockets/gitlab.socket", :backlog => 1024 -listen "/run/gitlab/gitlab.socket", :backlog => 1024 - -working_directory ENV["GITLAB_PATH"] - -pid ENV["UNICORN_PATH"] + "/tmp/pids/unicorn.pid" - -timeout 60 - -# combine Ruby 2.0.0dev or REE with "preload_app true" for memory savings -# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow -preload_app true -GC.respond_to?(:copy_on_write_friendly=) and - GC.copy_on_write_friendly = true - -check_client_connection false - -before_fork do |server, worker| - # the following is highly recommended for Rails + "preload_app true" - # as there's no need for the master process to hold a connection - defined?(ActiveRecord::Base) and - ActiveRecord::Base.connection.disconnect! - - # The following is only recommended for memory/DB-constrained - # installations. It is not needed if your system can house - # twice as many worker_processes as you have configured. - # - # This allows a new master process to incrementally - # phase out the old master process with SIGTTOU to avoid a - # thundering herd (especially in the "preload_app false" case) - # when doing a transparent upgrade. The last worker spawned - # will then kill off the old master process with a SIGQUIT. - old_pid = "#{server.config[:pid]}.oldbin" - if old_pid != server.pid - begin - sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU - Process.kill(sig, File.read(old_pid).to_i) - rescue Errno::ENOENT, Errno::ESRCH - end - end - - # Throttle the master from forking too quickly by sleeping. Due - # to the implementation of standard Unix signal handlers, this - # helps (but does not completely) prevent identical, repeated signals - # from being lost when the receiving process is busy. - # sleep 1 -end - -after_fork do |server, worker| - # per-process listener ports for debugging/admin/migrations - # addr = "127.0.0.1:#{9293 + worker.nr}" - # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true) - - # the following is *required* for Rails + "preload_app true", - defined?(ActiveRecord::Base) and - ActiveRecord::Base.establish_connection - - # reset prometheus client, this will cause any opened metrics files to be closed - defined?(::Prometheus::Client.reinitialize_on_pid_change) && - Prometheus::Client.reinitialize_on_pid_change - - # if preload_app is true, then you may also want to check and - # restart any other shared sockets/descriptors such as Memcached, - # and Redis. TokyoCabinet file handles are safe to reuse - # between any number of forked children (assuming your kernel - # correctly implements pread()/pwrite() system calls) -end diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 4086a11ce87..91a6cd90064 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -142,7 +142,7 @@ let gitlabEnv = { HOME = "${cfg.statePath}/home"; - UNICORN_PATH = "${cfg.statePath}/"; + PUMA_PATH = "${cfg.statePath}/"; GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/"; SCHEMA = "${cfg.statePath}/db/structure.sql"; GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads"; @@ -725,8 +725,6 @@ in { "L+ /run/gitlab/uploads - - - - ${cfg.statePath}/uploads" "L+ /run/gitlab/shell-config.yml - - - - ${pkgs.writeText "config.yml" (builtins.toJSON gitlabShellConfig)}" - - "L+ ${cfg.statePath}/config/unicorn.rb - - - - ${./defaultUnicornConfig.rb}" ]; systemd.services.gitlab-sidekiq = { @@ -873,7 +871,9 @@ in { set -eu chown --no-dereference '${cfg.user}':'${cfg.group}' '${cfg.statePath}'/* - chown --no-dereference '${cfg.user}':'${cfg.group}' '${cfg.statePath}'/config/* + if [[ ! -z "$(ls -A '${cfg.statePath}'/config/)" ]]; then + chown --no-dereference '${cfg.user}':'${cfg.group}' '${cfg.statePath}'/config/* + fi ''; preStart = '' set -eu @@ -956,7 +956,7 @@ in { "+${pkgs.writeShellScript "gitlab-pre-start-full-privileges" preStartFullPrivileges}" "${pkgs.writeShellScript "gitlab-pre-start" preStart}" ]; - ExecStart = "${cfg.packages.gitlab.rubyEnv}/bin/unicorn -c ${cfg.statePath}/config/unicorn.rb -E production"; + ExecStart = "${cfg.packages.gitlab.rubyEnv}/bin/puma -C ${cfg.statePath}/config/puma.rb -e production"; }; }; diff --git a/pkgs/applications/version-management/gitlab/default.nix b/pkgs/applications/version-management/gitlab/default.nix index ee6c8201fa3..b9c352a4eac 100644 --- a/pkgs/applications/version-management/gitlab/default.nix +++ b/pkgs/applications/version-management/gitlab/default.nix @@ -137,6 +137,7 @@ stdenv.mkDerivation { sed -i '/ask_to_continue/d' lib/tasks/gitlab/two_factor.rake sed -ri -e '/log_level/a config.logger = Logger.new(STDERR)' config/environments/production.rb + mv config/puma.rb.example config/puma.rb # Always require lib-files and application.rb through their store # path, not their relative state directory path. This gets rid of # warnings and means we don't have to link back to lib from the diff --git a/pkgs/applications/version-management/gitlab/remove-hardcoded-locations.patch b/pkgs/applications/version-management/gitlab/remove-hardcoded-locations.patch index fcb954e3884..83e3d7fe141 100644 --- a/pkgs/applications/version-management/gitlab/remove-hardcoded-locations.patch +++ b/pkgs/applications/version-management/gitlab/remove-hardcoded-locations.patch @@ -1,8 +1,8 @@ diff --git a/config/environments/production.rb b/config/environments/production.rb -index c5cbfcf64c..4d01f6fab8 100644 +index d9b3ee354b0..1eb0507488b 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb -@@ -70,10 +70,10 @@ Rails.application.configure do +@@ -69,10 +69,10 @@ config.action_mailer.delivery_method = :sendmail # Defaults to: @@ -11,17 +11,17 @@ index c5cbfcf64c..4d01f6fab8 100644 - # # arguments: '-i -t' - # # } + config.action_mailer.sendmail_settings = { -+ location: '/usr/sbin/sendmail', ++ location: '/run/wrappers/bin/sendmail', + arguments: '-i -t' + } config.action_mailer.perform_deliveries = true config.action_mailer.raise_delivery_errors = true diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example -index bd696a7f2c..44e3863736 100644 +index 92e7501d49d..4ee5a1127df 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example -@@ -590,7 +590,7 @@ production: &base +@@ -1168,7 +1168,7 @@ production: &base # CAUTION! # Use the default values unless you really know what you are doing git: @@ -31,10 +31,10 @@ index bd696a7f2c..44e3863736 100644 ## Webpack settings # If enabled, this will tell rails to serve frontend assets from the webpack-dev-server running diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb -index 0bea8a4f4b..290248547b 100644 +index bbed08f5044..2906e5c44af 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb -@@ -177,7 +177,7 @@ Settings.gitlab['ssh_user'] ||= Settings.gitlab['user'] +@@ -183,7 +183,7 @@ Settings.gitlab['user_home'] ||= begin Etc.getpwnam(Settings.gitlab['user']).dir rescue ArgumentError # no user configured @@ -43,7 +43,7 @@ index 0bea8a4f4b..290248547b 100644 end Settings.gitlab['time_zone'] ||= nil Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].nil? -@@ -507,7 +507,7 @@ Settings.backup['upload']['storage_class'] ||= nil +@@ -751,7 +751,7 @@ # Git # Settings['git'] ||= Settingslogic.new({}) @@ -52,37 +52,94 @@ index 0bea8a4f4b..290248547b 100644 # Important: keep the satellites.path setting until GitLab 9.0 at # least. This setting is fed to 'rm -rf' in +diff --git a/config/puma.rb.example b/config/puma.rb.example +index 9fc354a8fe8..2352ca9b58c 100644 +--- a/config/puma.rb.example ++++ b/config/puma.rb.example +@@ -5,12 +5,8 @@ + # The default is "config.ru". + # + rackup 'config.ru' +-pidfile '/home/git/gitlab/tmp/pids/puma.pid' +-state_path '/home/git/gitlab/tmp/pids/puma.state' +- +-stdout_redirect '/home/git/gitlab/log/puma.stdout.log', +- '/home/git/gitlab/log/puma.stderr.log', +- true ++pidfile ENV['PUMA_PATH'] + '/tmp/pids/puma.pid' ++state_path ENV['PUMA_PATH'] + '/tmp/pids/puma.state' + + # Configure "min" to be the minimum number of threads to use to answer + # requests and "max" the maximum. +@@ -31,12 +27,12 @@ queue_requests false + + # Bind the server to "url". "tcp://", "unix://" and "ssl://" are the only + # accepted protocols. +-bind 'unix:///home/git/gitlab/tmp/sockets/gitlab.socket' ++bind "unix://#{ENV['PUMA_PATH']}/tmp/sockets/gitlab.socket" + + workers 3 + +-require_relative "/home/git/gitlab/lib/gitlab/cluster/lifecycle_events" +-require_relative "/home/git/gitlab/lib/gitlab/cluster/puma_worker_killer_initializer" ++require_relative ENV['GITLAB_PATH'] + "lib/gitlab/cluster/lifecycle_events" ++require_relative ENV['GITLAB_PATH'] + "lib/gitlab/cluster/puma_worker_killer_initializer" + + on_restart do + # Signal application hooks that we're about to restart +@@ -80,7 +76,7 @@ if defined?(nakayoshi_fork) + end + + # Use json formatter +-require_relative "/home/git/gitlab/lib/gitlab/puma_logging/json_formatter" ++require_relative ENV['GITLAB_PATH'] + "lib/gitlab/puma_logging/json_formatter" + + json_formatter = Gitlab::PumaLogging::JSONFormatter.new + log_formatter do |str| diff --git a/lib/api/api.rb b/lib/api/api.rb -index e953f3d2ec..3a8d9f076b 100644 +index ada0da28749..8a3f5824008 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb -@@ -2,7 +2,7 @@ module API - class API < Grape::API +@@ -4,7 +4,7 @@ module API + class API < ::API::Base include APIGuard - LOG_FILENAME = Rails.root.join("log", "api_json.log") + LOG_FILENAME = File.join(ENV["GITLAB_LOG_PATH"], "api_json.log") - NO_SLASH_URL_PART_REGEX = %r{[^/]+} - PROJECT_ENDPOINT_REQUIREMENTS = { id: NO_SLASH_URL_PART_REGEX }.freeze + NO_SLASH_URL_PART_REGEX = %r{[^/]+}.freeze + NAMESPACE_OR_PROJECT_REQUIREMENTS = { id: NO_SLASH_URL_PART_REGEX }.freeze +diff --git a/lib/gitlab/authorized_keys.rb b/lib/gitlab/authorized_keys.rb +index 50cd15b7a10..3ac89e5b8e9 100644 +--- a/lib/gitlab/authorized_keys.rb ++++ b/lib/gitlab/authorized_keys.rb +@@ -157,7 +157,7 @@ def command(id) + raise KeyError, "Invalid ID: #{id.inspect}" + end + +- "#{File.join(Gitlab.config.gitlab_shell.path, 'bin', 'gitlab-shell')} #{id}" ++ "#{File.join('/run/current-system/sw/bin', 'gitlab-shell')} #{id}" + end + + def strip(key) diff --git a/lib/gitlab/logger.rb b/lib/gitlab/logger.rb -index a42e312b5d..ccaab9229e 100644 +index 89a4e36a232..ae379ffb27a 100644 --- a/lib/gitlab/logger.rb +++ b/lib/gitlab/logger.rb -@@ -26,7 +26,7 @@ module Gitlab +@@ -37,7 +37,7 @@ def self.build end def self.full_log_path - Rails.root.join("log", file_name) -+ File.join(ENV["GITLAB_LOG_PATH"], file_name) ++ File.join(ENV["GITLAB_LOG_PATH"], file_name) end def self.cache_key diff --git a/lib/gitlab/uploads_transfer.rb b/lib/gitlab/uploads_transfer.rb -index 7d7400bdab..cb25211d44 100644 +index e0e7084e27e..19fab855b90 100644 --- a/lib/gitlab/uploads_transfer.rb +++ b/lib/gitlab/uploads_transfer.rb -@@ -1,7 +1,7 @@ +@@ -3,7 +3,7 @@ module Gitlab class UploadsTransfer < ProjectTransfer def root_dir @@ -92,10 +149,10 @@ index 7d7400bdab..cb25211d44 100644 end end diff --git a/lib/system_check/app/log_writable_check.rb b/lib/system_check/app/log_writable_check.rb -index 3e0c436d6e..28cefc5514 100644 +index 2c108f0c18d..3a16ff52d01 100644 --- a/lib/system_check/app/log_writable_check.rb +++ b/lib/system_check/app/log_writable_check.rb -@@ -21,7 +21,7 @@ module SystemCheck +@@ -23,7 +23,7 @@ def show_error private def log_path @@ -105,10 +162,10 @@ index 3e0c436d6e..28cefc5514 100644 end end diff --git a/lib/system_check/app/uploads_directory_exists_check.rb b/lib/system_check/app/uploads_directory_exists_check.rb -index 7026d0ba07..c56e1f7ed9 100644 +index 54dff63ab61..882da702f29 100644 --- a/lib/system_check/app/uploads_directory_exists_check.rb +++ b/lib/system_check/app/uploads_directory_exists_check.rb -@@ -4,12 +4,13 @@ module SystemCheck +@@ -6,12 +6,13 @@ class UploadsDirectoryExistsCheck < SystemCheck::BaseCheck set_name 'Uploads directory exists?' def check? @@ -120,15 +177,15 @@ index 7026d0ba07..c56e1f7ed9 100644 + uploads_dir = ENV['GITLAB_UPLOADS_PATH'] || Rails.root.join('public/uploads') try_fixing_it( - "sudo -u #{gitlab_user} mkdir #{Rails.root}/public/uploads" -+ "sudo -u #{gitlab_user} mkdir #{uploads_dir}" ++ "sudo -u #{gitlab_user} mkdir #{uploads_dir}" ) for_more_information( - see_installation_guide_section 'GitLab' + see_installation_guide_section('GitLab') diff --git a/lib/system_check/app/uploads_path_permission_check.rb b/lib/system_check/app/uploads_path_permission_check.rb -index 7df6c06025..bb447c16b2 100644 +index 2e1cc687c43..ca69d63bcf6 100644 --- a/lib/system_check/app/uploads_path_permission_check.rb +++ b/lib/system_check/app/uploads_path_permission_check.rb -@@ -25,7 +25,7 @@ module SystemCheck +@@ -27,7 +27,7 @@ def show_error private def rails_uploads_path @@ -138,10 +195,10 @@ index 7df6c06025..bb447c16b2 100644 def uploads_fullpath diff --git a/lib/system_check/app/uploads_path_tmp_permission_check.rb b/lib/system_check/app/uploads_path_tmp_permission_check.rb -index b276a81eac..070e3ebd81 100644 +index 567c7540777..29906b1c132 100644 --- a/lib/system_check/app/uploads_path_tmp_permission_check.rb +++ b/lib/system_check/app/uploads_path_tmp_permission_check.rb -@@ -33,7 +33,7 @@ module SystemCheck +@@ -35,7 +35,7 @@ def upload_path_tmp end def uploads_fullpath @@ -150,14 +207,3 @@ index b276a81eac..070e3ebd81 100644 end end end ---- a/lib/gitlab/authorized_keys.rb -+++ b/lib/gitlab/authorized_keys.rb -@@ -157,7 +157,7 @@ - raise KeyError, "Invalid ID: #{id.inspect}" - end - -- "#{File.join(Gitlab.config.gitlab_shell.path, 'bin', 'gitlab-shell')} #{id}" -+ "#{File.join('/run/current-system/sw/bin', 'gitlab-shell')} #{id}" - end - - def strip(key) From 2b3800b9c7776b674a29959f18dfc09b9c4a00c3 Mon Sep 17 00:00:00 2001 From: talyz Date: Thu, 18 Feb 2021 17:26:20 +0100 Subject: [PATCH 3/7] nixos/gitlab: Change default SMTP port, enable postfix only if used Change the default SMTP port to `25`, to better match the default address `localhost`. This gets rid of some error outputs in the test, where it fails to connect to localhost:465. Also, don't enable postfix by default unless it's actually useful to us. --- nixos/doc/manual/release-notes/rl-2105.xml | 7 +++++++ nixos/modules/services/misc/gitlab.nix | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2105.xml b/nixos/doc/manual/release-notes/rl-2105.xml index 302a6d3f374..6868cea4a77 100644 --- a/nixos/doc/manual/release-notes/rl-2105.xml +++ b/nixos/doc/manual/release-notes/rl-2105.xml @@ -523,6 +523,13 @@ self: super: as an hardware RNG, as it will automatically run the krngd task to periodically collect random data from the device and mix it into the kernel's RNG. + + The default SMTP port for GitLab has been changed to + 25 from its previous default of + 465. If you depended on this default, you + should now set the + option. + diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 91a6cd90064..0f01e36e691 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -424,7 +424,7 @@ in { port = mkOption { type = types.int; - default = 465; + default = 25; description = "Port of the SMTP server for Gitlab."; }; @@ -684,7 +684,7 @@ in { }; # Use postfix to send out mails. - services.postfix.enable = mkDefault true; + services.postfix.enable = mkDefault (cfg.smtp.enable && cfg.smtp.address == "localhost"); users.users.${cfg.user} = { group = cfg.group; From 53d9ec83ff6d9b2cff5d24029449efa8e839ce84 Mon Sep 17 00:00:00 2001 From: talyz Date: Thu, 18 Feb 2021 17:33:21 +0100 Subject: [PATCH 4/7] nixos/gitlab: postgresql: Make PSQL a function, run as superUser A function is more appropriate for this use. See http://mywiki.wooledge.org/BashFAQ/050 for reference. Also, we don't need to run the service as root: since we essentially run all commands as `services.postgresql.superUser` anyway, the whole service can just run as that user instead. --- nixos/modules/services/misc/gitlab.nix | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 0f01e36e691..609fbc92bd6 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -656,29 +656,35 @@ in { systemd.services.gitlab-postgresql = let pgsql = config.services.postgresql; in mkIf databaseActuallyCreateLocally { after = [ "postgresql.service" ]; wantedBy = [ "multi-user.target" ]; - path = [ pgsql.package ]; + path = [ + pgsql.package + pkgs.util-linux + ]; script = '' set -eu - PSQL="${pkgs.util-linux}/bin/runuser -u ${pgsql.superUser} -- psql --port=${toString pgsql.port}" + PSQL() { + psql --port=${toString pgsql.port} "$@" + } - $PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${cfg.databaseName}'" | grep -q 1 || $PSQL -tAc 'CREATE DATABASE "${cfg.databaseName}" OWNER "${cfg.databaseUsername}"' - current_owner=$($PSQL -tAc "SELECT pg_catalog.pg_get_userbyid(datdba) FROM pg_catalog.pg_database WHERE datname = '${cfg.databaseName}'") + PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${cfg.databaseName}'" | grep -q 1 || PSQL -tAc 'CREATE DATABASE "${cfg.databaseName}" OWNER "${cfg.databaseUsername}"' + current_owner=$(PSQL -tAc "SELECT pg_catalog.pg_get_userbyid(datdba) FROM pg_catalog.pg_database WHERE datname = '${cfg.databaseName}'") if [[ "$current_owner" != "${cfg.databaseUsername}" ]]; then - $PSQL -tAc 'ALTER DATABASE "${cfg.databaseName}" OWNER TO "${cfg.databaseUsername}"' + PSQL -tAc 'ALTER DATABASE "${cfg.databaseName}" OWNER TO "${cfg.databaseUsername}"' if [[ -e "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}" ]]; then echo "Reassigning ownership of database ${cfg.databaseName} to user ${cfg.databaseUsername} failed on last boot. Failing..." exit 1 fi touch "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}" - $PSQL "${cfg.databaseName}" -tAc "REASSIGN OWNED BY \"$current_owner\" TO \"${cfg.databaseUsername}\"" + PSQL "${cfg.databaseName}" -tAc "REASSIGN OWNED BY \"$current_owner\" TO \"${cfg.databaseUsername}\"" rm "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}" fi - $PSQL '${cfg.databaseName}' -tAc "CREATE EXTENSION IF NOT EXISTS pg_trgm" - $PSQL '${cfg.databaseName}' -tAc "CREATE EXTENSION IF NOT EXISTS btree_gist;" + PSQL '${cfg.databaseName}' -tAc "CREATE EXTENSION IF NOT EXISTS pg_trgm" + PSQL '${cfg.databaseName}' -tAc "CREATE EXTENSION IF NOT EXISTS btree_gist;" ''; serviceConfig = { + User = pgsql.superUser; Type = "oneshot"; }; }; From 9d4e76dd462972313203b04d1415d5888bf869bf Mon Sep 17 00:00:00 2001 From: talyz Date: Thu, 18 Feb 2021 17:47:07 +0100 Subject: [PATCH 5/7] nixos/gitlab: Make gitlab.service's PreStart into two new services Make the config initialization script run in gitlab.service's PreStart section into two new services, `gitlab-config.service` and `gitlab-db-config.service`. Other services can then depend on the config scripts they need instead of unnecessarily depending on `gitlab.service`. This makes the reason for the configured service dependencies much clearer and should also reduce the restart time of the `gitlab` service quite a lot, when triggered manually. Also, set up stricter service dependencies, using `bindsTo`, to ensure that if a service fails or is stopped, its dependants are also stopped. For example, if we're using the `postgresql` service and it's stopped, `gitlab.service` and `gitlab-sidekiq.service`, which depend on it to function, should also be stopped. --- nixos/modules/services/misc/gitlab.nix | 263 +++++++++++++++---------- 1 file changed, 164 insertions(+), 99 deletions(-) diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 609fbc92bd6..37073bfd386 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -655,6 +655,7 @@ in { # here. systemd.services.gitlab-postgresql = let pgsql = config.services.postgresql; in mkIf databaseActuallyCreateLocally { after = [ "postgresql.service" ]; + bindsTo = [ "postgresql.service" ]; wantedBy = [ "multi-user.target" ]; path = [ pgsql.package @@ -686,6 +687,7 @@ in { serviceConfig = { User = pgsql.superUser; Type = "oneshot"; + RemainAfterExit = true; }; }; @@ -733,8 +735,150 @@ in { "L+ /run/gitlab/shell-config.yml - - - - ${pkgs.writeText "config.yml" (builtins.toJSON gitlabShellConfig)}" ]; + + systemd.services.gitlab-config = { + wantedBy = [ "multi-user.target" ]; + path = with pkgs; [ + jq + openssl + replace + git + ]; + serviceConfig = { + Type = "oneshot"; + User = cfg.user; + Group = cfg.group; + TimeoutSec = "infinity"; + Restart = "on-failure"; + WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab"; + RemainAfterExit = true; + + ExecStartPre = let + preStartFullPrivileges = '' + shopt -s dotglob nullglob + set -eu + + chown --no-dereference '${cfg.user}':'${cfg.group}' '${cfg.statePath}'/* + if [[ -n "$(ls -A '${cfg.statePath}'/config/)" ]]; then + chown --no-dereference '${cfg.user}':'${cfg.group}' '${cfg.statePath}'/config/* + fi + ''; + in "+${pkgs.writeShellScript "gitlab-pre-start-full-privileges" preStartFullPrivileges}"; + + ExecStart = pkgs.writeShellScript "gitlab-config" '' + set -eu + + umask u=rwx,g=rx,o= + + cp -f ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION + rm -rf ${cfg.statePath}/db/* + rm -f ${cfg.statePath}/lib + find '${cfg.statePath}/config/' -maxdepth 1 -mindepth 1 -type d -execdir rm -rf {} \; + cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config + cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db + ln -sf ${extraGitlabRb} ${cfg.statePath}/config/initializers/extra-gitlab.rb + + ${cfg.packages.gitlab-shell}/bin/install + + ${optionalString cfg.smtp.enable '' + install -m u=rw ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb + ${optionalString (cfg.smtp.passwordFile != null) '' + smtp_password=$(<'${cfg.smtp.passwordFile}') + replace-literal -e '@smtpPassword@' "$smtp_password" '${cfg.statePath}/config/initializers/smtp_settings.rb' + ''} + ''} + + ( + umask u=rwx,g=,o= + + openssl rand -hex 32 > ${cfg.statePath}/gitlab_shell_secret + + rm -f '${cfg.statePath}/config/database.yml' + + ${if cfg.databasePasswordFile != null then '' + export db_password="$(<'${cfg.databasePasswordFile}')" + + if [[ -z "$db_password" ]]; then + >&2 echo "Database password was an empty string!" + exit 1 + fi + + jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \ + '.production.password = $ENV.db_password' \ + >'${cfg.statePath}/config/database.yml' + '' + else '' + jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \ + >'${cfg.statePath}/config/database.yml' + '' + } + + ${utils.genJqSecretsReplacementSnippet + gitlabConfig + "${cfg.statePath}/config/gitlab.yml" + } + + rm -f '${cfg.statePath}/config/secrets.yml' + + export secret="$(<'${cfg.secrets.secretFile}')" + export db="$(<'${cfg.secrets.dbFile}')" + export otp="$(<'${cfg.secrets.otpFile}')" + export jws="$(<'${cfg.secrets.jwsFile}')" + jq -n '{production: {secret_key_base: $ENV.secret, + otp_key_base: $ENV.otp, + db_key_base: $ENV.db, + openid_connect_signing_key: $ENV.jws}}' \ + > '${cfg.statePath}/config/secrets.yml' + ) + + # We remove potentially broken links to old gitlab-shell versions + rm -Rf ${cfg.statePath}/repositories/**/*.git/hooks + + git config --global core.autocrlf "input" + ''; + }; + }; + + systemd.services.gitlab-db-config = { + after = [ "gitlab-config.service" "gitlab-postgresql.service" "postgresql.service" ]; + bindsTo = [ + "gitlab-config.service" + ] ++ optional (cfg.databaseHost == "") "postgresql.service" + ++ optional databaseActuallyCreateLocally "gitlab-postgresql.service"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + User = cfg.user; + Group = cfg.group; + TimeoutSec = "infinity"; + Restart = "on-failure"; + WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab"; + RemainAfterExit = true; + + ExecStart = pkgs.writeShellScript "gitlab-db-config" '' + set -eu + umask u=rwx,g=rx,o= + + initial_root_password="$(<'${cfg.initialRootPasswordFile}')" + ${gitlab-rake}/bin/gitlab-rake gitlab:db:configure GITLAB_ROOT_PASSWORD="$initial_root_password" \ + GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}' > /dev/null + ''; + }; + }; + systemd.services.gitlab-sidekiq = { - after = [ "network.target" "redis.service" "gitlab.service" ]; + after = [ + "network.target" + "redis.service" + "postgresql.service" + "gitlab-config.service" + "gitlab-db-config.service" + ]; + bindsTo = [ + "redis.service" + "gitlab-config.service" + "gitlab-db-config.service" + ] ++ optional (cfg.databaseHost == "") "postgresql.service"; wantedBy = [ "multi-user.target" ]; environment = gitlabEnv; path = with pkgs; [ @@ -761,8 +905,8 @@ in { }; systemd.services.gitaly = { - after = [ "network.target" "gitlab.service" ]; - bindsTo = [ "gitlab.service" ]; + after = [ "network.target" "gitlab-config.service" ]; + bindsTo = [ "gitlab-config.service" ]; wantedBy = [ "multi-user.target" ]; path = with pkgs; [ openssh @@ -786,7 +930,8 @@ in { systemd.services.gitlab-pages = mkIf (gitlabConfig.production.pages.enabled or false) { description = "GitLab static pages daemon"; - after = [ "network.target" "redis.service" "gitlab.service" ]; # gitlab.service creates configs + after = [ "network.target" "gitlab-config.service" ]; + bindsTo = [ "gitlab-config.service" ]; wantedBy = [ "multi-user.target" ]; path = [ pkgs.unzip ]; @@ -835,7 +980,8 @@ in { systemd.services.gitlab-mailroom = mkIf (gitlabConfig.production.incoming_email.enabled or false) { description = "GitLab incoming mail daemon"; - after = [ "network.target" "redis.service" "gitlab.service" ]; # gitlab.service creates configs + after = [ "network.target" "redis.service" "gitlab-config.service" ]; + bindsTo = [ "gitlab-config.service" ]; wantedBy = [ "multi-user.target" ]; environment = gitlabEnv; serviceConfig = { @@ -845,14 +991,24 @@ in { User = cfg.user; Group = cfg.group; - ExecStart = "${cfg.packages.gitlab.rubyEnv}/bin/bundle exec mail_room -c ${cfg.packages.gitlab}/share/gitlab/config.dist/mail_room.yml"; + ExecStart = "${cfg.packages.gitlab.rubyEnv}/bin/bundle exec mail_room -c ${cfg.statePath}/config/mail_room.yml"; WorkingDirectory = gitlabEnv.HOME; }; }; systemd.services.gitlab = { - after = [ "gitlab-workhorse.service" "network.target" "gitlab-postgresql.service" "redis.service" ]; - requires = [ "gitlab-sidekiq.service" ]; + after = [ + "gitlab-workhorse.service" + "network.target" + "redis.service" + "gitlab-config.service" + "gitlab-db-config.service" + ]; + bindsTo = [ + "redis.service" + "gitlab-config.service" + "gitlab-db-config.service" + ] ++ optional (cfg.databaseHost == "") "postgresql.service"; wantedBy = [ "multi-user.target" ]; environment = gitlabEnv; path = with pkgs; [ @@ -871,97 +1027,6 @@ in { TimeoutSec = "infinity"; Restart = "on-failure"; WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab"; - ExecStartPre = let - preStartFullPrivileges = '' - shopt -s dotglob nullglob - set -eu - - chown --no-dereference '${cfg.user}':'${cfg.group}' '${cfg.statePath}'/* - if [[ ! -z "$(ls -A '${cfg.statePath}'/config/)" ]]; then - chown --no-dereference '${cfg.user}':'${cfg.group}' '${cfg.statePath}'/config/* - fi - ''; - preStart = '' - set -eu - - umask u=rwx,g=rx,o= - - cp -f ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION - rm -rf ${cfg.statePath}/db/* - rm -f ${cfg.statePath}/lib - find '${cfg.statePath}/config/' -maxdepth 1 -mindepth 1 -type d -execdir rm -rf {} \; - cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config - cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db - ln -sf ${extraGitlabRb} ${cfg.statePath}/config/initializers/extra-gitlab.rb - - ${cfg.packages.gitlab-shell}/bin/install - - ${optionalString cfg.smtp.enable '' - install -m u=rw ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb - ${optionalString (cfg.smtp.passwordFile != null) '' - smtp_password=$(<'${cfg.smtp.passwordFile}') - ${pkgs.replace}/bin/replace-literal -e '@smtpPassword@' "$smtp_password" '${cfg.statePath}/config/initializers/smtp_settings.rb' - ''} - ''} - - ( - umask u=rwx,g=,o= - - ${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/gitlab_shell_secret - - if [[ -h '${cfg.statePath}/config/database.yml' ]]; then - rm '${cfg.statePath}/config/database.yml' - fi - - ${if cfg.databasePasswordFile != null then '' - export db_password="$(<'${cfg.databasePasswordFile}')" - - if [[ -z "$db_password" ]]; then - >&2 echo "Database password was an empty string!" - exit 1 - fi - - ${pkgs.jq}/bin/jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \ - '.production.password = $ENV.db_password' \ - >'${cfg.statePath}/config/database.yml' - '' - else '' - ${pkgs.jq}/bin/jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \ - >'${cfg.statePath}/config/database.yml' - '' - } - - ${utils.genJqSecretsReplacementSnippet - gitlabConfig - "${cfg.statePath}/config/gitlab.yml" - } - - rm -f '${cfg.statePath}/config/secrets.yml' - - export secret="$(<'${cfg.secrets.secretFile}')" - export db="$(<'${cfg.secrets.dbFile}')" - export otp="$(<'${cfg.secrets.otpFile}')" - export jws="$(<'${cfg.secrets.jwsFile}')" - ${pkgs.jq}/bin/jq -n '{production: {secret_key_base: $ENV.secret, - otp_key_base: $ENV.otp, - db_key_base: $ENV.db, - openid_connect_signing_key: $ENV.jws}}' \ - > '${cfg.statePath}/config/secrets.yml' - ) - - initial_root_password="$(<'${cfg.initialRootPasswordFile}')" - ${gitlab-rake}/bin/gitlab-rake gitlab:db:configure GITLAB_ROOT_PASSWORD="$initial_root_password" \ - GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}' > /dev/null - - # We remove potentially broken links to old gitlab-shell versions - rm -Rf ${cfg.statePath}/repositories/**/*.git/hooks - - ${pkgs.git}/bin/git config --global core.autocrlf "input" - ''; - in [ - "+${pkgs.writeShellScript "gitlab-pre-start-full-privileges" preStartFullPrivileges}" - "${pkgs.writeShellScript "gitlab-pre-start" preStart}" - ]; ExecStart = "${cfg.packages.gitlab.rubyEnv}/bin/puma -C ${cfg.statePath}/config/puma.rb -e production"; }; From f38c601b54613b13d1ad50f26fb3b5037685b165 Mon Sep 17 00:00:00 2001 From: talyz Date: Thu, 18 Feb 2021 18:21:29 +0100 Subject: [PATCH 6/7] nixosTests.gitlab: Test pages and mailroom service start Make sure that the `gitlab-pages` and `gitlab-mailroom` services at least start. --- nixos/tests/gitlab.nix | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/nixos/tests/gitlab.nix b/nixos/tests/gitlab.nix index ba085338944..baad675229c 100644 --- a/nixos/tests/gitlab.nix +++ b/nixos/tests/gitlab.nix @@ -11,6 +11,8 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : with lib; { nodes = { gitlab = { ... }: { + imports = [ common/user-account.nix ]; + virtualisation.memorySize = if pkgs.stdenv.is64bit then 4096 else 2047; systemd.services.gitlab.serviceConfig.Restart = mkForce "no"; systemd.services.gitlab-workhorse.serviceConfig.Restart = mkForce "no"; @@ -27,11 +29,31 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : with lib; { }; }; + services.dovecot2 = { + enable = true; + enableImap = true; + }; + services.gitlab = { enable = true; databasePasswordFile = pkgs.writeText "dbPassword" "xo0daiF4"; initialRootPasswordFile = pkgs.writeText "rootPassword" initialRootPassword; smtp.enable = true; + extraConfig = { + incoming_email = { + enabled = true; + mailbox = "inbox"; + address = "alice@localhost"; + user = "alice"; + password = "foobar"; + host = "localhost"; + port = 143; + }; + pages = { + enabled = true; + host = "localhost"; + }; + }; secrets = { secretFile = pkgs.writeText "secret" "r8X9keSKynU7p4aKlh4GO1Bo77g5a7vj"; otpFile = pkgs.writeText "otpsecret" "Zu5hGx3YvQx40DvI8WoZJQpX2paSDOlG"; @@ -64,12 +86,16 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : with lib; { in '' gitlab.start() + gitlab.wait_for_unit("gitaly.service") gitlab.wait_for_unit("gitlab-workhorse.service") + gitlab.wait_for_unit("gitlab-pages.service") + gitlab.wait_for_unit("gitlab-mailroom.service") gitlab.wait_for_unit("gitlab.service") gitlab.wait_for_unit("gitlab-sidekiq.service") gitlab.wait_for_file("/var/gitlab/state/tmp/sockets/gitlab.socket") gitlab.wait_until_succeeds("curl -sSf http://gitlab/users/sign_in") + gitlab.succeed( "curl -isSf http://gitlab | grep -i location | grep -q http://gitlab/users/sign_in" ) From ca725e7fcdde8ec5cfcd9b470eb96fcae2ec720d Mon Sep 17 00:00:00 2001 From: talyz Date: Thu, 25 Feb 2021 11:33:06 +0100 Subject: [PATCH 7/7] nixos/gitlab: Add `gitlab.target` To make it easier to start and stop all GitLab services, introduce `gitlab.target` which wants all services (meaning they will start with it) and which all services are part of (meaning they will stop with it). --- nixos/modules/services/misc/gitlab.nix | 32 ++++++++++++++++++-------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 37073bfd386..1d45af36349 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -641,6 +641,11 @@ in { environment.systemPackages = [ pkgs.git gitlab-rake gitlab-rails cfg.packages.gitlab-shell ]; + systemd.targets.gitlab = { + description = "Common target for all GitLab services."; + wantedBy = [ "multi-user.target" ]; + }; + # Redis is required for the sidekiq queue runner. services.redis.enable = mkDefault true; @@ -656,7 +661,8 @@ in { systemd.services.gitlab-postgresql = let pgsql = config.services.postgresql; in mkIf databaseActuallyCreateLocally { after = [ "postgresql.service" ]; bindsTo = [ "postgresql.service" ]; - wantedBy = [ "multi-user.target" ]; + wantedBy = [ "gitlab.target" ]; + partOf = [ "gitlab.target" ]; path = [ pgsql.package pkgs.util-linux @@ -737,7 +743,8 @@ in { systemd.services.gitlab-config = { - wantedBy = [ "multi-user.target" ]; + wantedBy = [ "gitlab.target" ]; + partOf = [ "gitlab.target" ]; path = with pkgs; [ jq openssl @@ -845,7 +852,8 @@ in { "gitlab-config.service" ] ++ optional (cfg.databaseHost == "") "postgresql.service" ++ optional databaseActuallyCreateLocally "gitlab-postgresql.service"; - wantedBy = [ "multi-user.target" ]; + wantedBy = [ "gitlab.target" ]; + partOf = [ "gitlab.target" ]; serviceConfig = { Type = "oneshot"; User = cfg.user; @@ -879,7 +887,8 @@ in { "gitlab-config.service" "gitlab-db-config.service" ] ++ optional (cfg.databaseHost == "") "postgresql.service"; - wantedBy = [ "multi-user.target" ]; + wantedBy = [ "gitlab.target" ]; + partOf = [ "gitlab.target" ]; environment = gitlabEnv; path = with pkgs; [ postgresqlPackage @@ -907,7 +916,8 @@ in { systemd.services.gitaly = { after = [ "network.target" "gitlab-config.service" ]; bindsTo = [ "gitlab-config.service" ]; - wantedBy = [ "multi-user.target" ]; + wantedBy = [ "gitlab.target" ]; + partOf = [ "gitlab.target" ]; path = with pkgs; [ openssh procps # See https://gitlab.com/gitlab-org/gitaly/issues/1562 @@ -932,7 +942,8 @@ in { description = "GitLab static pages daemon"; after = [ "network.target" "gitlab-config.service" ]; bindsTo = [ "gitlab-config.service" ]; - wantedBy = [ "multi-user.target" ]; + wantedBy = [ "gitlab.target" ]; + partOf = [ "gitlab.target" ]; path = [ pkgs.unzip ]; @@ -951,7 +962,8 @@ in { systemd.services.gitlab-workhorse = { after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; + wantedBy = [ "gitlab.target" ]; + partOf = [ "gitlab.target" ]; path = with pkgs; [ exiftool git @@ -982,7 +994,8 @@ in { description = "GitLab incoming mail daemon"; after = [ "network.target" "redis.service" "gitlab-config.service" ]; bindsTo = [ "gitlab-config.service" ]; - wantedBy = [ "multi-user.target" ]; + wantedBy = [ "gitlab.target" ]; + partOf = [ "gitlab.target" ]; environment = gitlabEnv; serviceConfig = { Type = "simple"; @@ -1009,7 +1022,8 @@ in { "gitlab-config.service" "gitlab-db-config.service" ] ++ optional (cfg.databaseHost == "") "postgresql.service"; - wantedBy = [ "multi-user.target" ]; + wantedBy = [ "gitlab.target" ]; + partOf = [ "gitlab.target" ]; environment = gitlabEnv; path = with pkgs; [ postgresqlPackage