Improve Mattermost OIDC mobile bridge installer
This commit is contained in:
@@ -35,6 +35,7 @@ SYMLINK_CHANGED=false
|
||||
UNIT_CREATED=false
|
||||
ROLLBACK_TARGET=""
|
||||
LEGACY_BRIDGE_BACKUP=""
|
||||
REINSTALL_BRIDGE_BACKUP=""
|
||||
|
||||
|
||||
# ----------
|
||||
@@ -162,6 +163,7 @@ ask_yes_no() {
|
||||
rollback_bridge() {
|
||||
local rollback_failed=false
|
||||
local rollback_path=""
|
||||
local rollback_tmp_link="${MM_BRIDGE_LINK}.rollback.$$"
|
||||
|
||||
[[ "$SYMLINK_CHANGED" = true ]] || return 0
|
||||
|
||||
@@ -189,11 +191,12 @@ rollback_bridge() {
|
||||
|
||||
if [[ "$rollback_failed" = true ]]; then
|
||||
error "Initial-install cleanup was not fully successful. Please check '${MM_BRIDGE_LINK}', '${MM_BRIDGE_SERVICE}' and nginx manually."
|
||||
return 1
|
||||
else
|
||||
info "Initial activation was removed. The new binary, source directory and unit file were kept for analysis."
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
warn "The new bridge did not pass all checks. Trying to restore the previous installation."
|
||||
|
||||
@@ -205,25 +208,85 @@ rollback_bridge() {
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$ROLLBACK_TARGET" && -e "$rollback_path" ]]; then
|
||||
ln -s "$ROLLBACK_TARGET" "${MM_BRIDGE_LINK}.rollback.$$" >> "$log_file" 2>&1 \
|
||||
&& mv -Tf "${MM_BRIDGE_LINK}.rollback.$$" "$MM_BRIDGE_LINK" >> "$log_file" 2>&1 \
|
||||
|| rollback_failed=true
|
||||
elif [[ -n "$LEGACY_BRIDGE_BACKUP" && -f "$LEGACY_BRIDGE_BACKUP" ]]; then
|
||||
rm -f "$MM_BRIDGE_LINK" >> "$log_file" 2>&1
|
||||
cp -a "$LEGACY_BRIDGE_BACKUP" "$MM_BRIDGE_LINK" >> "$log_file" 2>&1 || rollback_failed=true
|
||||
# A same-version reinstall replaces the file referenced by ROLLBACK_TARGET.
|
||||
# Restore that saved binary first; changing the symlink alone would otherwise
|
||||
# still point to the newly installed (and possibly broken) binary.
|
||||
if [[ -n "$REINSTALL_BRIDGE_BACKUP" ]]; then
|
||||
echononl "Restore previous same-version bridge binary.."
|
||||
_restore_tmp="${MM_BRIDGE_BIN}.restore.$$"
|
||||
rm -f "$_restore_tmp"
|
||||
|
||||
if [[ -f "$REINSTALL_BRIDGE_BACKUP" ]] \
|
||||
&& cp -a "$REINSTALL_BRIDGE_BACKUP" "$_restore_tmp" >> "$log_file" 2>&1 \
|
||||
&& mv -Tf "$_restore_tmp" "$MM_BRIDGE_BIN" >> "$log_file" 2>&1 ; then
|
||||
echo_ok
|
||||
else
|
||||
rm -f "$_restore_tmp"
|
||||
echo_failed
|
||||
error "Could not restore '${REINSTALL_BRIDGE_BACKUP}' to '${MM_BRIDGE_BIN}'."
|
||||
rollback_failed=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$rollback_failed" = false ]]; then
|
||||
systemctl restart "$MM_BRIDGE_SERVICE" >> "$log_file" 2>&1 || rollback_failed=true
|
||||
if [[ -n "$ROLLBACK_TARGET" && -e "$rollback_path" ]]; then
|
||||
echononl "Restore previous stable bridge symlink.."
|
||||
rm -f "$rollback_tmp_link"
|
||||
if ln -s "$ROLLBACK_TARGET" "$rollback_tmp_link" >> "$log_file" 2>&1 \
|
||||
&& mv -Tf "$rollback_tmp_link" "$MM_BRIDGE_LINK" >> "$log_file" 2>&1 ; then
|
||||
echo_ok
|
||||
else
|
||||
rm -f "$rollback_tmp_link"
|
||||
echo_failed
|
||||
error "Could not restore stable bridge symlink '${MM_BRIDGE_LINK}' -> '${ROLLBACK_TARGET}'."
|
||||
rollback_failed=true
|
||||
fi
|
||||
elif [[ -n "$LEGACY_BRIDGE_BACKUP" && -f "$LEGACY_BRIDGE_BACKUP" ]]; then
|
||||
echononl "Restore previous legacy bridge binary.."
|
||||
if rm -f "$MM_BRIDGE_LINK" >> "$log_file" 2>&1 \
|
||||
&& cp -a "$LEGACY_BRIDGE_BACKUP" "$MM_BRIDGE_LINK" >> "$log_file" 2>&1 ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
error "Could not restore legacy bridge binary from '${LEGACY_BRIDGE_BACKUP}'."
|
||||
rollback_failed=true
|
||||
fi
|
||||
else
|
||||
error "No usable rollback target was found."
|
||||
rollback_failed=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$rollback_failed" = false ]]; then
|
||||
echononl "Restart previous Mattermost OIDC Mobile Bridge service.."
|
||||
if systemctl restart "$MM_BRIDGE_SERVICE" >> "$log_file" 2>&1 ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
error "Could not restart '${MM_BRIDGE_SERVICE}' after rollback."
|
||||
rollback_failed=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$NGINX_CHANGED" = true && -n "$NGINX_BACKUP" && -f "$NGINX_BACKUP" ]]; then
|
||||
echononl "Restore previous nginx configuration.."
|
||||
if cp -a "$NGINX_BACKUP" "$NGINX_CONFIG_FILE" >> "$log_file" 2>&1 \
|
||||
&& nginx -t >> "$log_file" 2>&1 \
|
||||
&& systemctl reload nginx.service >> "$log_file" 2>&1 ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
error "Could not fully restore/reload the previous nginx configuration. Check nginx manually."
|
||||
rollback_failed=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$rollback_failed" = true ]]; then
|
||||
error "Automatic rollback failed. Please check '${MM_BRIDGE_LINK}' and '${MM_BRIDGE_SERVICE}' manually."
|
||||
error "Automatic rollback failed. Please check the bridge installation, '${MM_BRIDGE_SERVICE}' and nginx configuration manually."
|
||||
return 1
|
||||
else
|
||||
info "Previous bridge installation was restored and restarted."
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -777,7 +840,14 @@ blank_line
|
||||
|
||||
if [[ -e "$MM_BRIDGE_BIN" ]]; then
|
||||
echononl "Backup existing bridge binary.."
|
||||
if mv "$MM_BRIDGE_BIN" "${MM_BRIDGE_BIN}.${backup_date}" > "$log_file" 2>&1 ; then
|
||||
_bridge_binary_backup="${MM_BRIDGE_BIN}.${backup_date}"
|
||||
if mv "$MM_BRIDGE_BIN" "$_bridge_binary_backup" > "$log_file" 2>&1 ; then
|
||||
# During a same-version reinstall this backup is the actual rollback
|
||||
# payload. ROLLBACK_TARGET alone is insufficient because it names the
|
||||
# same versioned path that is about to be replaced.
|
||||
if [[ "$INSTALLATION_MODE" = "upgrade" && "$MM_NEW_VERSION" = "$MM_CURRENT_VERSION" ]]; then
|
||||
REINSTALL_BRIDGE_BACKUP="$_bridge_binary_backup"
|
||||
fi
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
@@ -797,6 +867,25 @@ else
|
||||
fatal "$(cat "$log_file")"
|
||||
fi
|
||||
|
||||
# On an initial installation the systemd unit below already references the
|
||||
# stable bridge symlink. Create that symlink before systemd-analyze verifies
|
||||
# the unit. During upgrades, keep the existing symlink untouched until the
|
||||
# final activation step.
|
||||
if [[ "$INSTALLATION_MODE" = "initial-installation" ]]; then
|
||||
echononl "Create initial stable bridge symlink.."
|
||||
_tmp_link="${MM_BRIDGE_LINK}.new.$$"
|
||||
rm -f "$_tmp_link"
|
||||
if ln -s "${MM_BRIDGE_NAME}-${MM_NEW_VERSION}" "$_tmp_link" > "$log_file" 2>&1 \
|
||||
&& mv -Tf "$_tmp_link" "$MM_BRIDGE_LINK" >> "$log_file" 2>&1 ; then
|
||||
SYMLINK_CHANGED=true
|
||||
echo_ok
|
||||
else
|
||||
rm -f "$_tmp_link"
|
||||
echo_failed
|
||||
fatal "$(cat "$log_file")"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Preserve an old unversioned installation before replacing it with a symlink.
|
||||
if [[ "$INSTALLATION_MODE" = "legacy-upgrade" ]]; then
|
||||
LEGACY_BRIDGE_BACKUP="${MM_BRIDGE_LINK}.${backup_date}"
|
||||
@@ -898,6 +987,20 @@ else
|
||||
fi
|
||||
|
||||
|
||||
# Ensure that the bridge service is enabled also when an existing unit is reused.
|
||||
echononl "Ensure '${MM_BRIDGE_SERVICE}' is enabled.."
|
||||
if systemctl is-enabled --quiet "$MM_BRIDGE_SERVICE" 2>/dev/null ; then
|
||||
echo_ok
|
||||
else
|
||||
if systemctl enable "$MM_BRIDGE_SERVICE" > "$log_file" 2>&1 ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
fatal "$(cat "$log_file")"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# ----------
|
||||
# Configure nginx if requested
|
||||
# ----------
|
||||
@@ -991,7 +1094,7 @@ NGINX_MOBILE_EOF
|
||||
fi
|
||||
|
||||
echononl "Insert missing bridge locations before Mattermost 'location /'.."
|
||||
python3 - "$NGINX_CONFIG_FILE" "$NGINX_INSERT_FILE" "$MM_BRIDGE_UPSTREAM" <<'PY_EOF' > "$log_file" 2>&1
|
||||
python3 - "$NGINX_CONFIG_FILE" "$NGINX_INSERT_FILE" "$MM_BRIDGE_UPSTREAM" "$_mm_site_host" <<'PY_EOF' > "$log_file" 2>&1
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
@@ -999,6 +1102,7 @@ from pathlib import Path
|
||||
config = Path(sys.argv[1])
|
||||
insert = Path(sys.argv[2]).read_text()
|
||||
upstream = sys.argv[3].rstrip("/")
|
||||
site_host = sys.argv[4].strip()
|
||||
text = config.read_text()
|
||||
|
||||
def matching_brace(data, open_pos):
|
||||
@@ -1032,25 +1136,53 @@ def matching_brace(data, open_pos):
|
||||
return pos
|
||||
return None
|
||||
|
||||
servers = []
|
||||
all_servers = []
|
||||
for match in re.finditer(r'(?m)^[ \t]*server[ \t]*\{', text):
|
||||
open_pos = text.find("{", match.start(), match.end())
|
||||
close_pos = matching_brace(text, open_pos)
|
||||
if close_pos is None:
|
||||
raise SystemExit("Could not parse nginx server block")
|
||||
body = text[open_pos + 1:close_pos]
|
||||
all_servers.append((open_pos + 1, close_pos, body))
|
||||
|
||||
def general_locations(body):
|
||||
return list(re.finditer(r'(?m)^[ \t]*location[ \t]+/[ \t]*\{', body))
|
||||
|
||||
# Prefer the Mattermost vHost identified by SiteURL/server_name. This works
|
||||
# with both direct proxy_pass targets and named nginx upstreams. The HTTP
|
||||
# redirect vHost is excluded because it has no general "location /" block.
|
||||
servers = []
|
||||
if site_host:
|
||||
for server in all_servers:
|
||||
body = server[2]
|
||||
names = []
|
||||
for match in re.finditer(r'(?m)^[ \t]*server_name[ \t]+([^;]+);', body):
|
||||
names.extend(match.group(1).split())
|
||||
if site_host in names and len(general_locations(body)) == 1:
|
||||
servers.append(server)
|
||||
|
||||
# Fallback for installations where SiteURL is unavailable: retain the old
|
||||
# direct-upstream detection.
|
||||
if not servers:
|
||||
proxy_re = re.compile(r'proxy_pass\s+' + re.escape(upstream) + r'/?\s*;')
|
||||
if proxy_re.search(body):
|
||||
servers.append((open_pos + 1, close_pos, body))
|
||||
servers = [
|
||||
server for server in all_servers
|
||||
if proxy_re.search(server[2]) and len(general_locations(server[2])) == 1
|
||||
]
|
||||
|
||||
if len(servers) != 1:
|
||||
raise SystemExit(f"Expected exactly one server block proxying to {upstream}, found {len(servers)}")
|
||||
if site_host:
|
||||
raise SystemExit(
|
||||
f"Expected exactly one Mattermost server block for server_name "
|
||||
f"{site_host} with a general 'location /', found {len(servers)}"
|
||||
)
|
||||
raise SystemExit(
|
||||
f"Expected exactly one server block proxying to {upstream} "
|
||||
f"with a general 'location /', found {len(servers)}"
|
||||
)
|
||||
|
||||
body_start, body_end, body = servers[0]
|
||||
locations = list(re.finditer(r'(?m)^[ \t]*location[ \t]+/[ \t]*\{', body))
|
||||
if len(locations) != 1:
|
||||
raise SystemExit(f"Expected exactly one general 'location /' in Mattermost server block, found {len(locations)}")
|
||||
|
||||
locations = general_locations(body)
|
||||
insert_pos = body_start + locations[0].start()
|
||||
text = text[:insert_pos] + insert + text[insert_pos:]
|
||||
config.write_text(text)
|
||||
@@ -1089,6 +1221,12 @@ blank_line
|
||||
echo -e "\033[37m\033[1mActivate Mattermost OIDC Mobile Bridge..\033[m"
|
||||
blank_line
|
||||
|
||||
if [[ "$INSTALLATION_MODE" = "initial-installation" ]] \
|
||||
&& [[ -L "$MM_BRIDGE_LINK" ]] \
|
||||
&& [[ "$(readlink "$MM_BRIDGE_LINK")" = "${MM_BRIDGE_NAME}-${MM_NEW_VERSION}" ]]; then
|
||||
echononl "Keep initial stable bridge symlink to '${MM_BRIDGE_NAME}-${MM_NEW_VERSION}'.."
|
||||
echo_ok
|
||||
else
|
||||
echononl "Switch stable bridge symlink to '${MM_BRIDGE_NAME}-${MM_NEW_VERSION}'.."
|
||||
_tmp_link="${MM_BRIDGE_LINK}.new.$$"
|
||||
rm -f "$_tmp_link"
|
||||
@@ -1101,6 +1239,7 @@ else
|
||||
echo_failed
|
||||
fatal "$(cat "$log_file")"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$NGINX_CHANGED" = true ]]; then
|
||||
echononl "Reload nginx service.."
|
||||
@@ -1109,14 +1248,32 @@ if [[ "$NGINX_CHANGED" = true ]]; then
|
||||
else
|
||||
echo_failed
|
||||
error "nginx reload failed. Restoring the previous nginx configuration."
|
||||
|
||||
nginx_rollback_failed=false
|
||||
|
||||
cp -a "$NGINX_CONFIG_FILE" "${NGINX_CONFIG_FILE}.failed-${backup_date}" 2>/dev/null || true
|
||||
if cp -a "$NGINX_BACKUP" "$NGINX_CONFIG_FILE" >> "$log_file" 2>&1 && nginx -t >> "$log_file" 2>&1 && systemctl reload nginx.service >> "$log_file" 2>&1 ; then
|
||||
|
||||
if cp -a "$NGINX_BACKUP" "$NGINX_CONFIG_FILE" >> "$log_file" 2>&1 \
|
||||
&& nginx -t >> "$log_file" 2>&1 \
|
||||
&& systemctl reload nginx.service >> "$log_file" 2>&1 ; then
|
||||
info "Previous nginx configuration was restored and reloaded."
|
||||
NGINX_CHANGED=false
|
||||
else
|
||||
error "Could not fully restore/reload the previous nginx configuration. Check nginx manually."
|
||||
nginx_rollback_failed=true
|
||||
fi
|
||||
|
||||
if rollback_bridge ; then
|
||||
bridge_rollback_failed=false
|
||||
else
|
||||
bridge_rollback_failed=true
|
||||
fi
|
||||
|
||||
if [[ "$nginx_rollback_failed" = false && "$bridge_rollback_failed" = false ]]; then
|
||||
fatal "Installation of Mattermost OIDC Mobile Bridge ${MM_NEW_VERSION} failed because nginx could not be reloaded.\n The previous nginx configuration and bridge installation were successfully restored."
|
||||
else
|
||||
fatal "Installation of Mattermost OIDC Mobile Bridge ${MM_NEW_VERSION} failed because nginx could not be reloaded.\n The previous system state could not be fully restored.\n\n Manual intervention is required."
|
||||
fi
|
||||
rollback_bridge
|
||||
fatal "nginx reload failed after adding the Mattermost OIDC Mobile Bridge locations."
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1125,20 +1282,29 @@ if systemctl restart "$MM_BRIDGE_SERVICE" > "$log_file" 2>&1 ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
rollback_bridge
|
||||
fatal "Failed to restart '${MM_BRIDGE_SERVICE}'."
|
||||
if rollback_bridge ; then
|
||||
fatal "Installation of Mattermost OIDC Mobile Bridge ${MM_NEW_VERSION} failed because '${MM_BRIDGE_SERVICE}' could not be restarted.\n The previous bridge installation was successfully restored."
|
||||
else
|
||||
fatal "Installation of Mattermost OIDC Mobile Bridge ${MM_NEW_VERSION} failed because '${MM_BRIDGE_SERVICE}' could not be restarted.\n The previous installation could not be restored.\n\n Manual intervention is required."
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! bridge_test ; then
|
||||
rollback_bridge
|
||||
fatal "Mattermost OIDC Mobile Bridge functional test failed."
|
||||
if rollback_bridge ; then
|
||||
fatal "Installation of Mattermost OIDC Mobile Bridge ${MM_NEW_VERSION} failed.\n The previous bridge installation was successfully restored."
|
||||
else
|
||||
fatal "Installation of Mattermost OIDC Mobile Bridge ${MM_NEW_VERSION} failed\n and the previous installation could not be restored.\n\n Manual intervention is required."
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$NGINX_CONFIG_FILE" ]]; then
|
||||
if [[ -n "$MM_SITE_URL" ]]; then
|
||||
if ! nginx_e2e_test ; then
|
||||
rollback_bridge
|
||||
fatal "Mattermost OIDC Mobile Bridge end-to-end test through nginx failed."
|
||||
if rollback_bridge ; then
|
||||
fatal "Installation of Mattermost OIDC Mobile Bridge ${MM_NEW_VERSION} failed during the nginx end-to-end test.\n The previous bridge installation was successfully restored."
|
||||
else
|
||||
fatal "Installation of Mattermost OIDC Mobile Bridge ${MM_NEW_VERSION} failed during the nginx end-to-end test\n and the previous installation could not be restored.\n\n Manual intervention is required."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
warn "Mattermost SiteURL could not be read from '${MM_CONFIG_FILE}'. End-to-end nginx tests were skipped."
|
||||
@@ -1146,6 +1312,27 @@ if [[ -n "$NGINX_CONFIG_FILE" ]]; then
|
||||
fi
|
||||
|
||||
|
||||
# ----------
|
||||
# Remove redundant same-version backup after successful tests
|
||||
# ----------
|
||||
|
||||
if [[ -n "$REINSTALL_BRIDGE_BACKUP" && -f "$REINSTALL_BRIDGE_BACKUP" ]]; then
|
||||
if cmp -s "$REINSTALL_BRIDGE_BACKUP" "$MM_BRIDGE_BIN"; then
|
||||
echononl "Remove identical same-version bridge backup.."
|
||||
if rm -f "$REINSTALL_BRIDGE_BACKUP" > "$log_file" 2>&1 ; then
|
||||
echo_ok
|
||||
REINSTALL_BRIDGE_BACKUP=""
|
||||
else
|
||||
echo_failed
|
||||
warn "Could not remove redundant bridge backup '${REINSTALL_BRIDGE_BACKUP}'."
|
||||
fi
|
||||
else
|
||||
echononl "Keep different same-version bridge backup.."
|
||||
echo_ok
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# ----------
|
||||
# Final information
|
||||
# ----------
|
||||
|
||||
Reference in New Issue
Block a user