21700: Work around incomplete `bundle cache` bug
[arvados.git] / build / rails-package-scripts / postinst.sh
1 #!/bin/sh
2 # Copyright (C) The Arvados Authors. All rights reserved.
3 #
4 # SPDX-License-Identifier: AGPL-3.0
5
6 # This code runs after package variable definitions and step2.sh.
7
8 set -e
9
10 DATABASE_READY=1
11 APPLICATION_READY=1
12
13 report_not_ready() {
14     local ready_flag="$1"; shift
15     local config_file="$1"; shift
16     if [ "1" != "$ready_flag" ]; then cat >&2 <<EOF
17
18 PLEASE NOTE:
19
20 The $PACKAGE_NAME package was not configured completely because
21 $config_file needs some tweaking.
22 Please refer to the documentation at
23 <$DOC_URL> for more details.
24
25 When $(basename "$config_file") has been modified,
26 reconfigure or reinstall this package.
27
28 EOF
29     fi
30 }
31
32 report_web_service_warning() {
33     local warning="$1"; shift
34     cat >&2 <<EOF
35
36 WARNING: $warning.
37
38 To override, set the WEB_SERVICE environment variable to the name of the service
39 hosting the Rails server.
40
41 For Debian-based systems, then reconfigure this package with dpkg-reconfigure.
42
43 For RPM-based systems, then reinstall this package.
44
45 EOF
46 }
47
48 run_and_report() {
49     # Usage: run_and_report ACTION_MSG CMD
50     # This is the usual wrapper that prints ACTION_MSG, runs CMD, then writes
51     # a message about whether CMD succeeded or failed.  Returns the exit code
52     # of CMD.
53     local action_message="$1"; shift
54     local retcode=0
55     echo -n "$action_message..."
56     if "$@"; then
57         echo " done."
58     else
59         retcode=$?
60         echo " failed."
61     fi
62     return $retcode
63 }
64
65 setup_confdirs() {
66     for confdir in "$@"; do
67         if [ ! -d "$confdir" ]; then
68             install -d -g "$WWW_OWNER" -m 0750 "$confdir"
69         fi
70     done
71 }
72
73 setup_conffile() {
74     # Usage: setup_conffile CONFFILE_PATH [SOURCE_PATH]
75     # Both paths are relative to RELEASE_CONFIG_PATH.
76     # This function will try to safely ensure that a symbolic link for
77     # the configuration file points from RELEASE_CONFIG_PATH to CONFIG_PATH.
78     # If SOURCE_PATH is given, this function will try to install that file as
79     # the configuration file in CONFIG_PATH, and return 1 if the file in
80     # CONFIG_PATH is unmodified from the source.
81     local conffile_relpath="$1"; shift
82     local conffile_source="$1"
83     local release_conffile="$RELEASE_CONFIG_PATH/$conffile_relpath"
84     local etc_conffile="$CONFIG_PATH/$(basename "$conffile_relpath")"
85
86     # Note that -h can return true and -e will return false simultaneously
87     # when the target is a dangling symlink.  We're okay with that outcome,
88     # so check -h first.
89     if [ ! -h "$release_conffile" ]; then
90         if [ ! -e "$release_conffile" ]; then
91             ln -s "$etc_conffile" "$release_conffile"
92         # If there's a config file in /var/www identical to the one in /etc,
93         # overwrite it with a symlink after porting its permissions.
94         elif cmp --quiet "$release_conffile" "$etc_conffile"; then
95             local ownership="$(stat -c "%u:%g" "$release_conffile")"
96             local owning_group="${ownership#*:}"
97             if [ 0 != "$owning_group" ]; then
98                 chgrp "$owning_group" "$CONFIG_PATH" /etc/arvados
99             fi
100             chown "$ownership" "$etc_conffile"
101             chmod --reference="$release_conffile" "$etc_conffile"
102             ln --force -s "$etc_conffile" "$release_conffile"
103         fi
104     fi
105
106     if [ -n "$conffile_source" ]; then
107         if [ ! -e "$etc_conffile" ]; then
108             install -g "$WWW_OWNER" -m 0640 \
109                     "$RELEASE_CONFIG_PATH/$conffile_source" "$etc_conffile"
110             return 1
111         # Even if $etc_conffile already existed, it might be unmodified from
112         # the source.  This is especially likely when a user installs, updates
113         # database.yml, then reconfigures before they update application.yml.
114         # Use cmp to be sure whether $etc_conffile is modified.
115         elif cmp --quiet "$RELEASE_CONFIG_PATH/$conffile_source" "$etc_conffile"; then
116             return 1
117         fi
118     fi
119 }
120
121 prepare_database() {
122   DB_MIGRATE_STATUS=`bin/rake db:migrate:status 2>&1 || true`
123   if echo "$DB_MIGRATE_STATUS" | grep -qF 'Schema migrations table does not exist yet.'; then
124       # The database exists, but the migrations table doesn't.
125       run_and_report "Setting up database" bin/rake \
126                      "$RAILSPKG_DATABASE_LOAD_TASK" db:seed
127   elif echo "$DB_MIGRATE_STATUS" | grep -q '^database: '; then
128       run_and_report "Running db:migrate" \
129                      bin/rake db:migrate
130   elif echo "$DB_MIGRATE_STATUS" | grep -q 'database .* does not exist'; then
131       if ! run_and_report "Running db:setup" \
132            bin/rake db:setup 2>/dev/null; then
133           echo "Warning: unable to set up database." >&2
134           DATABASE_READY=0
135       fi
136   else
137     echo "Warning: Database is not ready to set up. Skipping database setup." >&2
138     DATABASE_READY=0
139   fi
140 }
141
142 configure_version() {
143   if [ -n "$WEB_SERVICE" ]; then
144       SERVICE_MANAGER=$(guess_service_manager)
145   elif WEB_SERVICE=$(list_services_systemd | grep -E '^(nginx|httpd)'); then
146       SERVICE_MANAGER=systemd
147   elif WEB_SERVICE=$(list_services_service \
148                          | grep -Eo '\b(nginx|httpd)[^[:space:]]*'); then
149       SERVICE_MANAGER=service
150   fi
151
152   if [ -z "$WEB_SERVICE" ]; then
153     report_web_service_warning "Web service (Nginx or Apache) not found"
154   elif [ "$WEB_SERVICE" != "$(echo "$WEB_SERVICE" | head -n 1)" ]; then
155     WEB_SERVICE=$(echo "$WEB_SERVICE" | head -n 1)
156     report_web_service_warning \
157         "Multiple web services found.  Choosing the first one ($WEB_SERVICE)"
158   fi
159
160   if [ -e /etc/redhat-release ]; then
161       # Recognize any service that starts with "nginx"; e.g., nginx16.
162       if [ "$WEB_SERVICE" != "${WEB_SERVICE#nginx}" ]; then
163         WWW_OWNER=nginx
164       else
165         WWW_OWNER=apache
166       fi
167   else
168       # Assume we're on a Debian-based system for now.
169       # Both Apache and Nginx run as www-data by default.
170       WWW_OWNER=www-data
171   fi
172
173   echo
174   echo "Assumption: $WEB_SERVICE is configured to serve Rails from"
175   echo "            $RELEASE_PATH"
176   echo "Assumption: $WEB_SERVICE and passenger run as $WWW_OWNER"
177   echo
178
179   echo -n "Creating symlinks to configuration in $CONFIG_PATH ..."
180   setup_confdirs /etc/arvados "$CONFIG_PATH"
181   setup_conffile environments/production.rb environments/production.rb.example \
182       || true
183   setup_extra_conffiles
184   echo "... done."
185
186   # Before we do anything else, make sure some directories and files are in place
187   if [ ! -e $SHARED_PATH/log ]; then mkdir -p $SHARED_PATH/log; fi
188   if [ ! -e $RELEASE_PATH/tmp ]; then mkdir -p $RELEASE_PATH/tmp; fi
189   if [ ! -e $RELEASE_PATH/log ]; then ln -s $SHARED_PATH/log $RELEASE_PATH/log; fi
190   if [ ! -e $SHARED_PATH/log/production.log ]; then touch $SHARED_PATH/log/production.log; fi
191
192   cd "$RELEASE_PATH"
193   export RAILS_ENV=production
194
195   run_and_report "Installing bundler" gem install --conservative --version '~> 2.4.0' bundler
196
197   run_and_report "Running bundle config set --local path $SHARED_PATH/vendor_bundle" \
198                  bundle config set --local path "$SHARED_PATH/vendor_bundle"
199
200   # As of April 2024/Bundler 2.4, `bundle install` tends not to install gems
201   # which are already installed system-wide, which causes bundle activation to
202   # fail later. Work around this by installing all gems manually.
203   local gem_dir="$SHARED_PATH/vendor_bundle/ruby/$(ruby -e 'puts RUBY_VERSION')"
204   find vendor/cache -maxdepth 1 -name '*.gem' -print0 \
205       | run_and_report "Installing bundle gems" \
206                        xargs -0r gem install --quiet --install-dir="$gem_dir"
207   run_and_report "Running bundle install" bundle install --prefer-local --quiet
208   run_and_report "Verifying bundle is complete" bundle exec true
209
210   echo -n "Ensuring directory and file permissions ..."
211   # Ensure correct ownership of a few files
212   chown "$WWW_OWNER:" $RELEASE_PATH/config/environment.rb
213   chown "$WWW_OWNER:" $RELEASE_PATH/config.ru
214   chown "$WWW_OWNER:" $RELEASE_PATH/Gemfile.lock
215   chown -R "$WWW_OWNER:" $RELEASE_PATH/tmp || true
216   chown -R "$WWW_OWNER:" $SHARED_PATH/log
217   # Make sure postgres doesn't try to use a pager.
218   export PAGER=
219   case "$RAILSPKG_DATABASE_LOAD_TASK" in
220       # db:structure:load was deprecated in Rails 6.1 and shouldn't be used.
221       db:schema:load | db:structure:load)
222           chown "$WWW_OWNER:" $RELEASE_PATH/db/schema.rb || true
223           chown "$WWW_OWNER:" $RELEASE_PATH/db/structure.sql || true
224           ;;
225   esac
226   chmod 644 $SHARED_PATH/log/*
227   chmod -R 2775 $RELEASE_PATH/tmp || true
228   echo "... done."
229
230   if [ -n "$RAILSPKG_DATABASE_LOAD_TASK" ]; then
231       prepare_database
232   fi
233
234   if [ -e /etc/arvados/config.yml ]; then
235       # warn about config errors (deprecated/removed keys from
236       # previous version, etc)
237       run_and_report "Checking configuration for completeness" \
238                      bin/rake config:check || APPLICATION_READY=0
239   else
240       APPLICATION_READY=0
241   fi
242
243   chown -R "$WWW_OWNER:" $RELEASE_PATH/tmp
244
245   setup_before_nginx_restart
246
247   if [ -n "$SERVICE_MANAGER" ]; then
248       service_command "$SERVICE_MANAGER" restart "$WEB_SERVICE"
249   fi
250 }
251
252 if [ "$1" = configure ]; then
253   # This is a debian-based system
254   configure_version
255 elif [ "$1" = "0" ] || [ "$1" = "1" ] || [ "$1" = "2" ]; then
256   # This is an rpm-based system
257   configure_version
258 fi
259
260 report_not_ready "$APPLICATION_READY" "/etc/arvados/config.yml"