Skip to content

Commit dd27c2f

Browse files
committed
Fixed hub upgrade PG migration failing with Permission denied on old binaries
Ticket: ENT-14899 Changelog: Title (cherry picked from commit 5f80295)
1 parent aaf6fef commit dd27c2f

4 files changed

Lines changed: 94 additions & 2 deletions

File tree

‎packaging/common/cfengine-hub/postinstall.sh‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,9 @@ migrate_db_using_dump_file() {
594594
safe_mv "$PREFIX/share" postgresql "$PREFIX/share.new"
595595
# restore old backup
596596
safe_cp "$BACKUP_DIR" data "$PREFIX/state/pg"
597-
safe_cp "$BACKUP_DIR" bin "$PREFIX"
597+
safe_cp_nolink "$BACKUP_DIR" bin "$PREFIX"
598+
# Ensure restored old binaries are executable by cfpostgres.
599+
chmod 755 "$PREFIX/bin"/* 2>/dev/null || true
598600
on_files cp "$BACKUP_DIR/lib" "$PREFIX/lib"
599601
safe_cp "$BACKUP_DIR/lib" postgresql "$PREFIX/lib"
600602
safe_cp "$BACKUP_DIR/share" postgresql "$PREFIX/share"
@@ -708,6 +710,14 @@ do_migration() {
708710
else
709711
DEBUG=0
710712
fi
713+
714+
# cfpostgres runs the old binaries during the migration (ENT-14899)
715+
if ! su cfpostgres -c "test -x $BACKUP_DIR/bin/pg_ctl"; then
716+
cf_console echo "Warning: cfpostgres can't execute the old PostgreSQL binaries, setting them to 755:"
717+
cf_console ls -l "$BACKUP_DIR/bin"
718+
chmod 755 "$BACKUP_DIR/bin"/*
719+
fi
720+
711721
cd /tmp
712722
cf_console echo "Migrating database using pg_upgrade utility..."
713723
cf_console echo
@@ -1109,6 +1119,7 @@ if ! [ -f "$PREFIX/UPGRADED_FROM.txt" ] || egrep '3\.([0-6]\.|7\.0)' "$PREFIX/UP
11091119
# Versions <= 3.7.0 are unreliable in their daemon killing. Kill them one
11101120
# more time now that we have upgraded.
11111121
cf_console platform_service cfengine3 stop
1122+
terminate_cf_agents
11121123
fi
11131124

11141125
# Let's make sure all files and directories created above have correct SELinux

‎packaging/common/cfengine-hub/preinstall.sh‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ fi
141141
#stop the remaining services on upgrade
142142
if is_upgrade; then
143143
cf_console platform_service cfengine3 stop
144+
terminate_cf_agents
144145
# CFE-2278: Migrate to split units
145146
if use_systemd && [ -e /usr/lib/systemd/system/cfengine3-web.service ]; then
146147
# When using systemd, the services are split in two, and although both will
@@ -240,7 +241,13 @@ if migrating_postgres; then
240241
# the postgresql.conf file was modified, we should try to use it after migration
241242
cp -a "$BACKUP_DIR/data/postgresql.conf" "$BACKUP_DIR/data/postgresql.conf.modified"
242243
fi
243-
safe_cp "$PREFIX" bin "$BACKUP_DIR"
244+
# Copy, don't hardlink: policy managing perms under $PREFIX/state/pg (e.g.
245+
# CFE-951 in masterfiles) would otherwise change the live binaries too.
246+
safe_cp_nolink "$PREFIX" bin "$BACKUP_DIR"
247+
# Normalize executable permissions so cfpostgres can run old PG binaries
248+
# during migration, whatever mode damage the source already had.
249+
chmod 755 "$BACKUP_DIR/bin"/* 2>/dev/null || true
250+
chmod 711 "$BACKUP_DIR/bin" 2>/dev/null || true
244251
on_files cp "$PREFIX/lib" "$BACKUP_DIR/lib"
245252
safe_cp "$PREFIX/lib" postgresql "$BACKUP_DIR/lib"
246253
safe_cp "$PREFIX/share" postgresql "$BACKUP_DIR/share"

‎packaging/common/cfengine-hub/preremove.sh‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
cf_console platform_service cfengine3 stop
2+
terminate_cf_agents
23
if use_systemd && [ -e /usr/lib/systemd/system/cfengine3-web.service ]; then
34
# When using systemd, the services are split in two, and although both will
45
# stop due to the command above, the web part may only do so after some

‎packaging/common/script-templates/script-common.sh‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,64 @@ wait_for_cf_postgres_down() {
104104
cd /tmp && ! su cfpostgres -c "$PREFIX/bin/psql -l" >/dev/null 2>&1
105105
}
106106

107+
cf_agent_pids() {
108+
# Prints the PIDs of running cf-agent processes, except our own ancestors
109+
# (cf-agent may be running this package upgrade) and, where /proc tells
110+
# us, processes in other mount namespaces (containers).
111+
_ancestors=" "
112+
_pid=$$
113+
while [ -n "$_pid" ] && [ "$_pid" -gt 1 ]; do
114+
_ancestors="$_ancestors$_pid "
115+
_pid=$(ps -o ppid= -p "$_pid" 2>/dev/null | tr -d ' ')
116+
done
117+
_ns=$(readlink /proc/self/ns/mnt 2>/dev/null || true)
118+
for _pid in $(pgrep -x cf-agent); do
119+
case "$_ancestors" in
120+
*" $_pid "*) continue ;;
121+
esac
122+
if [ -z "$_ns" ] || [ "$(readlink /proc/$_pid/ns/mnt 2>/dev/null)" = "$_ns" ]; then
123+
echo "$_pid"
124+
fi
125+
done
126+
}
127+
128+
terminate_cf_agents() {
129+
# Call after stopping cf-execd. Stopping cf-execd doesn't stop an agent it
130+
# started (systemd KillMode=process), and that agent can keep changing
131+
# things, like permissions under state/pg (ENT-14899). Gives running agents
132+
# 1.5 times the average agent run time from promise_summary.log (150s if
133+
# unknown) to finish, then kills them. Does nothing without pgrep.
134+
command -v pgrep >/dev/null || return 0
135+
_pids=$(cf_agent_pids)
136+
if [ -z "$_pids" ]; then
137+
return 0
138+
fi
139+
_timeout=$(tail -n 20 "$PREFIX/promise_summary.log" 2>/dev/null |
140+
awk -F'[,:]' '$2 >= $1 { s += $2 - $1; n++ } END { if (n) printf "%d", s / n * 1.5 }')
141+
if [ "${_timeout:-0}" -le 0 ]; then
142+
_timeout=150
143+
fi
144+
cf_console echo "Waiting up to ${_timeout}s for cf-agent to finish:"
145+
cf_console ps -o pid,etime,args -p "$(echo $_pids | tr ' ' ',')" || true
146+
_waited=0
147+
while [ -n "$(cf_agent_pids)" ] && [ "$_waited" -lt "$_timeout" ]; do
148+
sleep 5
149+
_waited=$((_waited + 5))
150+
done
151+
# Killing an agent can let the shell cf-execd started it from run the next
152+
# one (update.cf, then promises.cf), so check a few times.
153+
for _try in 1 2 3; do
154+
_pids=$(cf_agent_pids)
155+
if [ -z "$_pids" ]; then
156+
return 0
157+
fi
158+
cf_console echo "Warning: killing cf-agent still running after ${_timeout}s:"
159+
cf_console ps -o pid,etime,args -p "$(echo $_pids | tr ' ' ',')" || true
160+
kill -KILL $_pids 2>/dev/null || true
161+
sleep 1
162+
done
163+
}
164+
107165
safe_cp() {
108166
# "safe" alternative to `cp`. Tries `cp -al` first, and if it fails - `cp -a`.
109167
# Deletes partially-copied files if copy operation fails.
@@ -136,6 +194,21 @@ safe_cp() {
136194
return 1
137195
}
138196

197+
safe_cp_nolink() {
198+
# Like `safe_cp`, but never creates hardlinks, so the copy does not share
199+
# mode and owner with the source. Args are same as in safe_cp.
200+
test "$#" -eq 3 || return 2
201+
from="$1"
202+
name="$2"
203+
to="$3"
204+
if cp -a "$from/$name" "$to"; then
205+
return 0
206+
fi
207+
echo "Copy failed, so removing partially-copied data and aborting"
208+
rm -rf "$to/$name"
209+
return 1
210+
}
211+
139212
safe_mv() {
140213
# "safe" alternative to `mv`. Executes `safe_cp` and deletes source dir if
141214
# that succeeds.

0 commit comments

Comments
 (0)