608 lines
24 KiB
Bash
608 lines
24 KiB
Bash
#!/bin/bash
|
||
# Ver: 1.5 by Endial Fang (endial@126.com)
|
||
#
|
||
# 应用通用业务处理函数
|
||
|
||
. /colovu/lib/libcommon.sh # 通用函数库
|
||
. /colovu/lib/libfile.sh
|
||
. /colovu/lib/libfs.sh
|
||
. /colovu/lib/liblog.sh
|
||
. /colovu/lib/libos.sh
|
||
. /colovu/lib/libservice.sh
|
||
. /colovu/lib/libvalidations.sh
|
||
|
||
# 检测应用相应的配置文件是否存在,如果不存在,则从默认配置文件目录拷贝一份
|
||
# 默认配置文件路径:/etc/${APP_NAME}
|
||
# 目标配置文件路径:/srv/conf/${APP_NAME}
|
||
# 参数:
|
||
# $1 - 目标路径
|
||
# $2 - 源路径
|
||
# $* - 基础路径下的文件及目录列表,以" "分割
|
||
# 例子:
|
||
# ensure_config_file_exist /etc/${APP_NAME} conf.d server.conf
|
||
app_ensure_config_file_exist() {
|
||
local -r dist_path="${1:?dist paths is missing}"
|
||
local -r base_path="${2:?source paths is missing}"
|
||
local f=""
|
||
|
||
shift 2
|
||
LOG_D "List to check in ${base_path}: $@"
|
||
while [ "$#" -gt 0 ]; do
|
||
f="${1}"
|
||
LOG_D " Process \"${f}\""
|
||
if [ -d "${base_path}/${f}" ]; then
|
||
[[ ! -d "${dist_path}/${f}" ]] && LOG_D " Create directory: ${dist_path}/${f}" && mkdir -p "${dist_path}/${f}"
|
||
[[ ! -z $(ls -A "${base_path}/${f}") ]] && app_ensure_config_file_exist "${dist_path}/${f}" "${base_path}/${f}" $(ls -A "${base_path}/${f}")
|
||
else
|
||
[[ ! -e "${dist_path}/${f}" ]] && LOG_D " Copy: ${base_path}/${f} to ${dist_path}" && cp "${base_path}/${f}" "${dist_path}"
|
||
fi
|
||
shift
|
||
done
|
||
}
|
||
|
||
# 检测以 "<PREFIX>" 开头的环境变量,并更新指定配置文件中对应配置项的值
|
||
# 如果需要全部转换为小写,可使用命令: tr '[:upper:]' '[:lower:]'
|
||
# 环境变量与配置项替换规则 : 环境变量中下划线 ==> 配置参数中特殊字符
|
||
# - "_" ==> "_"(下划线)
|
||
# - "__" ==> "."(半角点)
|
||
# - "___" ==> "-"(中划线)
|
||
#
|
||
# 变量:
|
||
# $1 - 配置文件
|
||
# $2 - <PREFIX>前缀(不含结束的"_")
|
||
app_configure_from_environment() {
|
||
local confFile="${1:?missing file}"
|
||
local envPrefix="${2:-APP_CFG}"
|
||
|
||
LOG_D "Configuration File: ${confFile}"
|
||
|
||
for var in $(eval echo \${!${envPrefix}_@}); do
|
||
key="$(echo "$var" | sed -e 's/^'${envPrefix}'_//g' -e 's/___/-/g' -e 's/__/./g' | tr '[:upper:]' '[:lower:]')"
|
||
value="${!var}"
|
||
LOG_D " ${key}: ${value}"
|
||
app_common_conf_set "${PG_CONF_FILE}" "$key" "$value"
|
||
done
|
||
}
|
||
|
||
# 将变量配置更新至配置文件
|
||
# 参数:
|
||
# $1 - 文件
|
||
# $2 - 变量
|
||
# $3 - 值(列表)
|
||
app_common_conf_set() {
|
||
local file="${1:?missing file}"
|
||
local key="${2:?missing key}"
|
||
local value="${3:?missing value}"
|
||
|
||
if grep -q "^#*${key}" "$file" >/dev/null; then
|
||
replace_in_file "$file" "^#*${key}\s*=.*$" "${key} = '${value}'" false
|
||
else
|
||
echo "\n${key} = '${value}'" >>"$file"
|
||
fi
|
||
}
|
||
|
||
# 使用环境变量中配置,更新配置文件
|
||
app_update_conf() {
|
||
LOG_I "Update configure files from PG_CFG_*..."
|
||
|
||
if (( PG_SYNCHRONOUS_REPLICAS_NUM > 0 )); then
|
||
app_common_conf_set "${PG_CONF_FILE}" "synchronous_standby_names" "${PG_SYNCHRONOUS_REPLICAS_METHOD} ${PG_SYNCHRONOUS_REPLICAS_NUM} (\"${PG_SYNCHRONOUS_REPLICAS_NAMES}\")"
|
||
fi
|
||
|
||
[[ "${PG_REPLICATION_MODE}" == "standby" ]] && pg_configure_recovery
|
||
|
||
app_configure_from_environment "${PG_CONF_FILE}" "PG_CFG"
|
||
|
||
pg_update_hba_conf
|
||
}
|
||
|
||
# 检测用户参数信息是否满足条件; 针对部分权限过于开放情况,打印提示信息
|
||
app_verify_minimum_env() {
|
||
local error_code=0
|
||
|
||
LOG_D "Validating settings in ENV vars..."
|
||
|
||
print_validation_error() {
|
||
LOG_E "$1"
|
||
error_code=1
|
||
}
|
||
|
||
empty_password_error() {
|
||
print_validation_error "The $1 environment variable is empty or not set. Set the environment variable ALLOW_ANONYMOUS=yes to allow the container to be started with blank passwords. This is recommended only for development."
|
||
}
|
||
|
||
# 检测认证设置。如果不允许匿名登录,检测登录用户名及密码是否设置
|
||
if is_boolean_yes "${ALLOW_ANONYMOUS}"; then
|
||
LOG_W "You have set the environment variable ALLOW_ANONYMOUS=${ALLOW_ANONYMOUS}. For safety reasons, do not use this flag in a production environment."
|
||
else
|
||
if [[ -z "${PG_PASSWORD}" ]]; then
|
||
empty_password_error "{PG_PASSWORD}"
|
||
fi
|
||
if (( ${#PG_PASSWORD} > 100 )); then
|
||
print_validation_error "The password cannot be longer than 100 characters. Set the environment variable PG_PASSWORD with a shorter value"
|
||
fi
|
||
if [[ -n "${PG_USERNAME}" ]] && [[ -z "${PG_PASSWORD}" ]]; then
|
||
empty_password_error "{PG_PASSWORD}"
|
||
fi
|
||
if [[ -n "${PG_USERNAME}" ]] && [[ "${PG_USERNAME}" != "postgres" ]] && [[ -n "${PG_PASSWORD}" ]] && [[ -z "${PG_DATABASE}" ]]; then
|
||
print_validation_error "In order to use a custom PostgreSQL user you need to set the environment variable PG_DATABASE as well"
|
||
fi
|
||
fi
|
||
|
||
if [[ "${PG_REPLICATION_MODE}" = "primary" ]]; then
|
||
if (( PG_SYNCHRONOUS_REPLICAS_NUM < 0 )); then
|
||
print_validation_error "The number of synchronous replicas cannot be less than 0. Set the environment variable PG_SYNCHRONOUS_REPLICAS_NUM"
|
||
fi
|
||
elif [[ "${PG_REPLICATION_MODE}" = "standby" ]]; then
|
||
if [[ -z "${PG_REPLICATION_HOST}" ]]; then
|
||
print_validation_error "Slave replication mode chosen without setting the environment variable PG_REPLICATION_HOST. Use it to indicate where the Master node is running"
|
||
fi
|
||
if [[ -z "${PG_REPLICATION_USER}" ]]; then
|
||
print_validation_error "Slave replication mode chosen without setting the environment variable PG_REPLICATION_USER. Make sure that the primary also has this parameter set"
|
||
fi
|
||
if [[ -n "${PG_REPLICATION_USER}" ]] && [[ -z "${PG_REPLICATION_PASSWORD}" ]]; then
|
||
empty_password_error "{PG_REPLICATION_PASSWORD}"
|
||
fi
|
||
else
|
||
print_validation_error "Invalid replication mode. Available options are 'primary/standby'"
|
||
fi
|
||
|
||
if is_boolean_yes "${PG_ENABLE_LDAP}" && [[ -n "${PG_LDAP_URL}" ]] && [[ -n "${PG_LDAP_SERVER}" ]]; then
|
||
empty_password_error "You can not set PG_LDAP_URL and PG_LDAP_SERVER at the same time. Check your LDAP configuration."
|
||
fi
|
||
|
||
if [[ "${PG_CFG_SSL:-off}" == "on" ]]; then
|
||
if [[ -z "${PG_CFG_ssl_cert_file:-}" ]]; then
|
||
print_validation_error "You must provide a X.509 certificate in order to use TLS"
|
||
elif [[ ! -f "${PG_CFG_ssl_cert_file}" ]]; then
|
||
print_validation_error "The X.509 certificate file in the specified path ${PG_CFG_ssl_cert_file} does not exist"
|
||
fi
|
||
if [[ -z "${PG_CFG_ssl_key_file:-}" ]]; then
|
||
print_validation_error "You must provide a private key in order to use TLS"
|
||
elif [[ ! -f "${PG_CFG_ssl_key_file}" ]]; then
|
||
print_validation_error "The private key file in the specified path ${PG_CFG_ssl_key_file} does not exist"
|
||
fi
|
||
if [[ -z "${PG_CFG_ssl_ca_file:-}" ]]; then
|
||
LOG_W "A CA X.509 certificate was not provided. Client verification will not be performed in TLS connections"
|
||
elif [[ ! -f "${PG_CFG_ssl_ca_file}" ]]; then
|
||
print_validation_error "The CA X.509 certificate file in the specified path ${PG_CFG_ssl_ca_file} does not exist"
|
||
fi
|
||
if [[ -n "${PG_CFG_ssl_crl_file:-}" ]] && [[ ! -f "${PG_CFG_ssl_crl_file}" ]]; then
|
||
print_validation_error "The CRL file in the specified path ${PG_CFG_ssl_crl_file} does not exist"
|
||
fi
|
||
if ! is_yes_no_value "${PG_CFG_ssl_prefer_server_ciphers:-}"; then
|
||
print_validation_error "The values allowed for prefer_server_ciphers are: on or off"
|
||
fi
|
||
fi
|
||
|
||
[[ "$error_code" -eq 0 ]] || exit "$error_code"
|
||
}
|
||
|
||
# 更改默认监听地址为 "*" 或 "0.0.0.0",以对容器外提供服务
|
||
app_enable_remote_connections() {
|
||
LOG_I "Modify default config to ENABLE external IP access"
|
||
|
||
app_common_conf_set "${PG_CONF_FILE}" "listen_addresses" "0.0.0.0"
|
||
}
|
||
|
||
# 更改默认监听地址为 "localhost" 或 "127.0.0.1",以禁止对容器外提供服务
|
||
app_disable_remote_connections() {
|
||
LOG_I "Modify default config to DISABLE external IP access"
|
||
|
||
app_common_conf_set "${PG_CONF_FILE}" "listen_addresses" "127.0.0.1"
|
||
}
|
||
|
||
# 以后台方式启动应用服务,并等待启动就绪
|
||
app_start_server_bg() {
|
||
LOG_I "Starting ${APP_NAME} in background..."
|
||
|
||
# 使用 pg_ctl 命令,以服务方式启动 PostgreSQL
|
||
local pg_ctl_cmd=$(command -v pg_ctl)
|
||
local pg_ctl_flags=("-D" "${PGDATA}")
|
||
pg_ctl_flags+=("-l" "${PG_CFG_LOG_DIRECTORY}/postgres_init.log")
|
||
#pg_ctl_flags+=("-o" "'-c listen_addresses=127.0.0.1 -p 5432'")
|
||
pg_ctl_flags+=("-w" "-t" "30")
|
||
|
||
LOG_D " extra arguments: ${pg_ctl_flags[@]}"
|
||
debug_execute "${pg_ctl_cmd}" "start" "${pg_ctl_flags[@]}"
|
||
}
|
||
|
||
# 停止应用服务
|
||
app_stop_server() {
|
||
LOG_I "Stopping ${APP_NAME} background service..."
|
||
|
||
# 使用 pg_ctl 命令关闭服务
|
||
local pg_ctl_cmd=$(command -v pg_ctl)
|
||
local pg_ctl_flags=("-D" "${PGDATA}")
|
||
pg_ctl_flags+=("-m" "fast")
|
||
pg_ctl_flags+=("-w" "-t" "30")
|
||
|
||
LOG_D " extra arguments: ${pg_ctl_flags[@]}"
|
||
debug_execute "${pg_ctl_cmd}" "stop" "${pg_ctl_flags[@]}"
|
||
}
|
||
|
||
# 检测应用服务是否在后台运行中
|
||
app_is_server_running() {
|
||
local pid
|
||
pid="$(get_pid_from_file ${PG_CFG_EXTERNAL_PID_FILE})"
|
||
LOG_D "Checking ${APP_NAME} running status with PID: ${pid}"
|
||
|
||
if [[ -n "${pid}" ]]; then
|
||
is_service_running "${pid}"
|
||
else
|
||
false
|
||
fi
|
||
}
|
||
|
||
app_is_server_not_running() {
|
||
if [[ app_is_server_running == false ]]; then
|
||
true
|
||
else
|
||
false
|
||
fi
|
||
}
|
||
|
||
# 清理初始化应用时生成的临时文件
|
||
app_clean_tmp_file() {
|
||
LOG_D "Clean ${APP_NAME} tmp files ..."
|
||
|
||
local -r -a files=(
|
||
"${PGDATA}/postmaster.pid"
|
||
"${PG_CFG_EXTERNAL_PID_FILE}"
|
||
)
|
||
|
||
for file in ${files[@]}; do
|
||
if [[ -f "$file" ]]; then
|
||
LOG_D " Remove $file"
|
||
rm -rf "$file"
|
||
fi
|
||
done
|
||
}
|
||
|
||
# 用户自定义的前置初始化操作,依次执行目录 preinit.d 中的初始化脚本
|
||
# 执行完毕后,生成以当前时间命名的'.tar'的文件,包含执行记录、已执行的脚本,同时删除已执行的脚本
|
||
app_custom_preinit() {
|
||
LOG_I "Process pre-init for ${APP_NAME}..."
|
||
|
||
# 检测用户配置文件目录是否存在 preinit.d 文件夹,如果存在,尝试执行目录中的初始化脚本
|
||
if [[ -d "${APP_CONF_DIR}/preinit.d" ]] && [[ -n $(find "${APP_CONF_DIR}/preinit.d/" -type f -regex ".*\.\(sh\)") ]]; then
|
||
echo "$(date '+%Y-%m-%d %H:%M:%S') : Init success." > "${APP_CONF_DIR}/preinit.d/preinit_info"
|
||
local tarFile="preinit-$(date '+%Y-%m-%d-%H%M%S').tar"
|
||
tar --remove-files -cf "${tarFile}" "${APP_CONF_DIR}/preinit.d/preinit_info"
|
||
|
||
for f in $(find "${APP_CONF_DIR}/preinit.d/" -type f -regex ".*\.\(sh\)" | sort); do
|
||
LOG_D " Process: ${f}"
|
||
process_init_files "${f}"
|
||
tar --remove-files -f "${tarFile}" -r "${f}"
|
||
done
|
||
fi
|
||
}
|
||
|
||
# 应用默认初始化操作
|
||
app_default_init() {
|
||
LOG_I "Process default init for ${APP_NAME}..."
|
||
app_clean_tmp_file
|
||
|
||
if is_dir_empty "${PGDATA}"; then
|
||
LOG_I "Deploying ${APP_NAME} from scratch..."
|
||
|
||
if [[ "${PG_REPLICATION_MODE}" == "primary" ]]; then
|
||
# 使用 initdb 工具初始化集群仓库(包含配置文件的初始化)
|
||
pg_primary_init_db
|
||
|
||
app_disable_remote_connections
|
||
app_start_server_bg
|
||
|
||
[[ "${PG_DATABASE}" != "postgres" ]] && pg_create_custom_database
|
||
|
||
# 为数据库授权;默认用户不为 postgres 时,需要创建管理员账户
|
||
LOG_D "Set password for postgres user"
|
||
if [[ "${PG_USERNAME}" == "postgres" ]]; then
|
||
[[ -n "${PG_PASSWORD}" ]] && pg_alter_postgres_user "${PG_PASSWORD}"
|
||
else
|
||
[[ -n "${PG_POSTGRES_PASSWORD}" ]] && pg_alter_postgres_user "${PG_POSTGRES_PASSWORD}"
|
||
pg_create_admin_user
|
||
fi
|
||
[[ -n "${PG_REPLICATION_USER:-}" ]] && pg_create_replication_user
|
||
|
||
app_stop_server
|
||
app_enable_remote_connections
|
||
else
|
||
pg_standby_init_db
|
||
fi
|
||
elif [[ -f "${PGDATA}/PG_VERSION" ]]; then
|
||
local -r db_version="$(< "${PGDATA}/PG_VERSION")"
|
||
local -r curr_version="$(pg_get_major_version)"
|
||
|
||
if [[ "$db_version" -ne "$curr_version" ]]; then
|
||
LOG_E "PGDATA directory is not empty with INCORRECT version, application version is $curr_version, while DB version is $db_version"
|
||
exit 1
|
||
fi
|
||
|
||
LOG_I "Deploying ${APP_NAME} with persisted data..."
|
||
else
|
||
LOG_E "${PGDATA} not empty"
|
||
exit 1
|
||
fi
|
||
|
||
# 根据环境变量配置,更新 postgresql.conf / pg_hba.conf / pg_ident.conf 等
|
||
# 注意:如果在数据库客户端内使用 ALTER 更新配置,则存储在 postgresql.auto.conf 文件中,优先级高于 postgresql.conf
|
||
app_update_conf
|
||
}
|
||
|
||
# 用户自定义的应用初始化操作,依次执行目录 initdb.d 中的初始化脚本
|
||
# 执行完毕后,生成以当前时间命名的'.tar'的文件,包含执行记录、已执行的脚本,同时删除已执行的脚本
|
||
app_custom_init() {
|
||
LOG_I "Process DB-init for ${APP_NAME}..."
|
||
|
||
# 检测用户配置文件目录是否存在 preinit.d 文件夹,如果存在,尝试执行目录中的初始化脚本
|
||
if [[ -d "${APP_CONF_DIR}/initdb.d" ]] && [[ -n $(find "${APP_CONF_DIR}/initdb.d/" -type f -regex ".*\.\(sh\|sql\|sql.gz\)") ]]; then
|
||
echo "$(date '+%Y-%m-%d %H:%M:%S') : Init success." > "${APP_CONF_DIR}/initdb.d/dbinit_info"
|
||
local tarFile="initdb-$(date '+%Y-%m-%d-%H%M%S').tar"
|
||
tar --remove-files -cf "${tarFile}" "${APP_CONF_DIR}/initdb.d/initdb_info"
|
||
|
||
# 禁用远程访问,并启动后台服务
|
||
app_disable_remote_connections
|
||
app_start_server_bg
|
||
|
||
for f in $(find "${APP_CONF_DIR}/initdb.d/" -type f -regex ".*\.\(sh\|sql\|sql.gz\)" | sort); do
|
||
case "$f" in
|
||
*.sh)
|
||
LOG_D " Process Shell: ${f}"
|
||
process_init_files "$f"
|
||
;;
|
||
*.sql)
|
||
LOG_D " Process SQL: ${f}";
|
||
pg_execute "${PG_DATABASE}" "${PG_INITSCRIPTS_USERNAME}" "${PG_INITSCRIPTS_PASSWORD}" < "$f"
|
||
;;
|
||
*.sql.gz)
|
||
LOG_D " Process SQLs in ${f}";
|
||
gunzip -c "$f" | pg_execute "${PG_DATABASE}" "${PG_INITSCRIPTS_USERNAME}" "${PG_INITSCRIPTS_PASSWORD}"
|
||
;;
|
||
*)
|
||
LOG_D " Ignoring $f"
|
||
;;
|
||
esac
|
||
|
||
tar --remove-files -f "${tarFile}" -r "${f}"
|
||
done
|
||
|
||
# 停止后台服务,并启用远程访问
|
||
app_stop_server
|
||
app_clean_tmp_file
|
||
app_enable_remote_connections
|
||
fi
|
||
}
|
||
|
||
# 获取软件主版本号
|
||
pg_get_major_version() {
|
||
psql --version | grep -oE "[0-9]+\.[0-9]+" | grep -oE "^[0-9]+"
|
||
}
|
||
|
||
# 根据环境变量,更新 HBA 配置文件
|
||
pg_update_hba_conf() {
|
||
LOG_I "Update pg_hba.conf with environment values..."
|
||
|
||
# 默认生成的配置中:
|
||
# - 默认数据库,local / host 访问策略为 trust,host IP range 为 127.0.0.1/32 及 ::1/128
|
||
# - replication 数据库,,local / host 访问策略为 trust,host IP range 为 127.0.0.1/32 及 ::1/128
|
||
|
||
pg_hba_update_host_auth
|
||
pg_hba_update_replication_auth
|
||
|
||
if is_boolean_yes "${PG_ENABLE_LDAP}"; then
|
||
pg_hba_allow_ldap_auth
|
||
fi
|
||
|
||
if [[ "${PG_CFG_SSL:-off}" == "on" ]]; then
|
||
pg_hba_allow_tls_connection
|
||
fi
|
||
}
|
||
|
||
# 更新 pg_hba.conf 文件,所有数据库仅允许基于密码认证的访问
|
||
pg_hba_update_host_auth() {
|
||
LOG_D " Update default host auth configs..."
|
||
|
||
local host_auth="trust"
|
||
if [[ -n "${PG_PASSWORD}" ]]; then
|
||
host_auth="md5"
|
||
fi
|
||
sed -i -E "s@(host)\s+(all)\s+(all)\s+([0-9\.\/]+)\s+.*@\1\t\2\t\3\t${PG_USER_IP4_RANGE}\t${host_auth}@g" "${PG_CFG_HBA_FILE}"
|
||
sed -i -E "s@(host)\s+(all)\s+(all)\s+([a-z0-9\/\:]+)\s+.*@\1\t\2\t\3\t${PG_USER_IP6_RANGE}\t${host_auth}@g" "${PG_CFG_HBA_FILE}"
|
||
}
|
||
|
||
# 更新 pg_hba.conf 文件,replication 仅允许基于密码认证的访问
|
||
pg_hba_update_replication_auth() {
|
||
LOG_D " Update replication host auth configs..."
|
||
|
||
local replication_auth="trust"
|
||
if [[ -n "${PG_REPLICATION_PASSWORD:-}" ]]; then
|
||
replication_auth="md5"
|
||
fi
|
||
sed -i -E "s@(host)\s+(replication)\s+(all)\s+([0-9\.\/]+)\s+.*@\1\t\2\t\3\t${PG_REPLICATION_IP4_RANGE}\t${replication_auth}@g" "${PG_CFG_HBA_FILE}"
|
||
sed -i -E "s@(host)\s+(replication)\s+(all)\s+([a-z0-9\/\:]+)\s+.*@\1\t\2\t\3\t${PG_REPLICATION_IP6_RANGE}\t${replication_auth}@g" "${PG_CFG_HBA_FILE}"
|
||
}
|
||
|
||
# 更新 pg_hba.conf 文件,增加 LDAP 配置;同时增加 postgres 用户的本地认证
|
||
pg_hba_allow_ldap_auth() {
|
||
LOG_I "Enabling LDAP authentication"
|
||
local ldap_configuration=""
|
||
|
||
if [[ -n "${PG_LDAP_URL}" ]]; then
|
||
ldap_configuration="ldapurl=\"${PG_LDAP_URL}\""
|
||
else
|
||
ldap_configuration="ldapserver=${PG_LDAP_SERVER}"
|
||
|
||
[[ -n "${PG_LDAP_PREFIX}" ]] && ldap_configuration+=" ldapprefix=\"${PG_LDAP_PREFIX}\""
|
||
[[ -n "${PG_LDAP_SUFFIX}" ]] && ldap_configuration+=" ldapsuffix=\"${PG_LDAP_SUFFIX}\""
|
||
[[ -n "${PG_LDAP_PORT}" ]] && ldap_configuration+=" ldapport=${PG_LDAP_PORT}"
|
||
[[ -n "${PG_LDAP_BASE_DN}" ]] && ldap_configuration+=" ldapbasedn=\"${PG_LDAP_BASE_DN}\""
|
||
[[ -n "${PG_LDAP_BIND_DN}" ]] && ldap_configuration+=" ldapbinddn=\"${PG_LDAP_BIND_DN}\""
|
||
[[ -n "${PG_LDAP_BIND_PASSWORD}" ]] && ldap_configuration+=" ldapbindpasswd=${PG_LDAP_BIND_PASSWORD}"
|
||
[[ -n "${PG_LDAP_SEARCH_ATTR}" ]] && ldap_configuration+=" ldapsearchattribute=${PG_LDAP_SEARCH_ATTR}"
|
||
[[ -n "${PG_LDAP_SEARCH_FILTER}" ]] && ldap_configuration+=" ldapsearchfilter=\"${PG_LDAP_SEARCH_FILTER}\""
|
||
[[ -n "${PG_LDAP_TLS}" ]] && ldap_configuration+=" ldaptls=${PG_LDAP_TLS}"
|
||
[[ -n "${PG_LDAP_SCHEME}" ]] && ldap_configuration+=" ldapscheme=${PG_LDAP_SCHEME}"
|
||
fi
|
||
|
||
sed -i -E "s@(host)\s+(all)\s+(all)\s+([0-9\.\/]+)\s+.*@\1\t\2\t\3\t${PG_REPLICATION_IP4_RANGE}\tldap ${ldap_configuration}@g" "${PG_CFG_HBA_FILE}"
|
||
sed -i -E "s@(host)\s+(all)\s+(all)\s+([a-z0-9\/\:]+)\s+.*@\1\t\2\t\3\t${PG_REPLICATION_IP6_RANGE}\tldap ${ldap_configuration}@g" "${PG_CFG_HBA_FILE}"
|
||
|
||
LOG_D " Add new postgres authentication when auth with LDAP"
|
||
local postgres_auth="trust"
|
||
if [[ -n "${PG_POSTGRES_PASSWORD}" ]]; then
|
||
postgres_auth="md5"
|
||
fi
|
||
if ! $(grep -iE "(host)\s+(all)\s+(postgres).*" "${PG_CFG_HBA_FILE}"); then
|
||
cat <<EOF >"${PG_CFG_HBA_FILE}"
|
||
|
||
host all postgres ${PG_REPLICATION_IP4_RANGE} ${postgres_auth}
|
||
host all postgres ${PG_REPLICATION_IP6_RANGE} ${postgres_auth}
|
||
EOF
|
||
fi
|
||
}
|
||
|
||
# 设置 pg_hba.conf 文件,增加 TLS 配置
|
||
pg_hba_allow_tls_connection() {
|
||
LOG_I "Enabling TLS client authentication"
|
||
|
||
cat <<"EOF" >>"${PG_CFG_HBA_FILE}"
|
||
|
||
hostssl all all ${PG_USER_IP4_RANGE} cert
|
||
hostssl all all ${PG_USER_IP6_RANGE} cert
|
||
EOF
|
||
}
|
||
|
||
# 为 Slava 模式工作的节点创建 recovery.conf 文件
|
||
pg_configure_recovery() {
|
||
LOG_I "Setting up streaming replication standby..."
|
||
|
||
# Recover 配置信息在不同版本保存位置不一样:
|
||
# 版本为12及以上时, Slave 节点配置保存在 postgresql.conf 文件中
|
||
# 版本低于12时, Slave 节点配置保存在 recover.conf 文件中
|
||
app_common_conf_set "${PG_CONF_FILE}" "primary_conninfo" "host=${PG_REPLICATION_HOST} port=${PG_REPLICATION_PORT} user=${PG_REPLICATION_USER} password=${PG_REPLICATION_PASSWORD} application_name=${PG_REPLICATION_APP_NAME} connect_timeout=${PG_REPLICATION_CONNECT_TIMEOUT}"
|
||
app_common_conf_set "${PG_CONF_FILE}" "promote_trigger_file" "/tmp/postgresql.trigger.${PG_REPLICATION_PORT}"
|
||
touch "${PGDATA}/standby.signal"
|
||
}
|
||
|
||
# 为默认的数据库用户 postgres 设置密码
|
||
# 参数:
|
||
# $1 - 用户密码
|
||
pg_alter_postgres_user() {
|
||
local -r escaped_password="${1//\'/\'\'}"
|
||
LOG_I "Changing password of postgres"
|
||
|
||
echo "ALTER ROLE postgres WITH PASSWORD '$escaped_password';" | pg_execute
|
||
if [[ -n "${PG_DB_CONNECTION_LIMIT}" ]]; then
|
||
echo "ALTER ROLE postgres WITH CONNECTION LIMIT ${PG_DB_CONNECTION_LIMIT};" | pg_execute
|
||
fi
|
||
}
|
||
|
||
# 为数据库 $PG_DATABASE 创建管理员账户
|
||
pg_create_admin_user() {
|
||
local -r escaped_password="${PG_PASSWORD//\'/\'\'}"
|
||
|
||
local connlimit_string=""
|
||
if [[ -n "${PG_USER_CONNECTION_LIMIT}" ]]; then
|
||
connlimit_string="CONNECTION LIMIT ${PG_USER_CONNECTION_LIMIT}"
|
||
fi
|
||
|
||
LOG_I "Creating user ${PG_USERNAME}"
|
||
echo "CREATE ROLE \"${PG_USERNAME}\" WITH LOGIN ${connlimit_string} CREATEDB PASSWORD '${escaped_password}';" | pg_execute
|
||
|
||
LOG_I "Granting access to \"${PG_USERNAME}\" to the database \"${PG_DATABASE}\""
|
||
echo "GRANT ALL PRIVILEGES ON DATABASE \"${PG_DATABASE}\" TO \"${PG_USERNAME}\"\;" | pg_execute "" "postgres" "${PG_POSTGRES_PASSWORD}"
|
||
}
|
||
|
||
# 为 primary-standby 复制模式创建用户
|
||
pg_create_replication_user() {
|
||
local -r escaped_password="${PG_REPLICATION_PASSWORD//\'/\'\'}"
|
||
LOG_I "Creating replication user ${PG_REPLICATION_USER}"
|
||
|
||
echo "CREATE ROLE \"${PG_REPLICATION_USER}\" WITH REPLICATION LOGIN ENCRYPTED PASSWORD '$escaped_password'" | pg_execute
|
||
}
|
||
|
||
# 创建用户自定义数据库 $PG_DATABASE
|
||
pg_create_custom_database() {
|
||
LOG_I "Creating custom database ${PG_DATABASE}"
|
||
|
||
echo "CREATE DATABASE \"${PG_DATABASE}\"" | pg_execute "" "postgres" "" "localhost"
|
||
}
|
||
|
||
# 使用运行中的 PostgreSQL 服务执行 SQL 操作
|
||
# 参数:
|
||
# $1 - 需要操作的数据库名
|
||
# $2 - 操作使用的用户名
|
||
# $3 - 操作用户密码
|
||
# $4 - 主机
|
||
# $5 - 端口
|
||
# $6 - 扩展参数 (如: -tA)
|
||
pg_execute() {
|
||
local -r db="${1:-}"
|
||
local -r user="${2:-postgres}"
|
||
local -r pass="${3:-}"
|
||
local -r host="${4:-localhost}"
|
||
local -r port="${5:-${PG_PORT_NUMBER}}"
|
||
local -r opts="${6:-}"
|
||
|
||
local cmd="$(command -v psql)"
|
||
local args=("-h" "$host" "-p" "$port" "-U" "$user")
|
||
[[ -n "$db" ]] && args+=("-d" "$db")
|
||
[[ -n "$opts" ]] && args+=("$opts")
|
||
|
||
LOG_D "Execute args: ${args[@]}"
|
||
debug_execute PGPASSWORD=$pass "${cmd}" "${args[@]}"
|
||
}
|
||
|
||
# 初始化 Master 节点数据库
|
||
pg_primary_init_db() {
|
||
LOG_I "Initializing PostgreSQL database"
|
||
|
||
local initdb_cmd="$(command -v initdb)"
|
||
local envExtraFlags=()
|
||
local initdb_args=()
|
||
if [[ -n "${PG_INITDB_ARGS}" ]]; then
|
||
read -r -a envExtraFlags <<< "${PG_INITDB_ARGS}"
|
||
initdb_args+=("${envExtraFlags[@]}")
|
||
fi
|
||
[[ "${PG_INITDB_WAL_DIR}" != "${PGDATA}/pg_wal" ]] && initdb_args+=("--waldir=${PG_INITDB_WAL_DIR}")
|
||
# 配置 local / host 访问策略都为 trust
|
||
initdb_args+=("--auth=trust")
|
||
|
||
LOG_D " extra initdb arguments: ${initdb_args[*]}"
|
||
debug_execute "${initdb_cmd}" -E UTF8 -D "${PGDATA}" -U "postgres" "${initdb_args[@]}"
|
||
}
|
||
|
||
# 初始化 Slave 节点数据库
|
||
pg_standby_init_db() {
|
||
LOG_I "Waiting for replication primary to accept connections..."
|
||
local check_cmd="$(command -v pg_isready)"
|
||
local check_args=("-h" "${PG_REPLICATION_HOST}" "-p" "${PG_REPLICATION_PORT}" "-U" "${PG_REPLICATION_USER}" "-d" "${PG_DATABASE}" "-t" "1")
|
||
|
||
local ready_counter=${PG_INIT_MAX_TIMEOUT}
|
||
while ! PGPASSWORD=${PG_REPLICATION_PASSWORD} "${check_cmd}" "${check_args[@]}" >/dev/null 2>&1;do
|
||
sleep 1
|
||
ready_counter=$(( ready_counter - 1 ))
|
||
if (( ready_counter <= 0 )); then
|
||
LOG_E "PostgreSQL primary is not ready after ${PG_INIT_MAX_TIMEOUT} seconds"
|
||
exit 1
|
||
fi
|
||
done
|
||
|
||
LOG_I "Replicating the database from node primary..."
|
||
local backup_cmd="$(command -v pg_basebackup)"
|
||
local backup_args=("-D" "${PGDATA}")
|
||
backup_args+=("-h" "${PG_REPLICATION_HOST}" "-p" "${PG_REPLICATION_PORT}" "-U" "${PG_REPLICATION_USER}" "-W")
|
||
backup_args+=("-C" "-S" "${PG_REPLICATION_APP_NAME}")
|
||
[[ "${PG_INITDB_WAL_DIR}" != "${PGDATA}/pg_wal" ]] && backup_args+=("--waldir=${PG_INITDB_WAL_DIR}")
|
||
backup_args+=("-X" "stream" "-v" "-R" "-F" "plain")
|
||
|
||
local replication_counter=0
|
||
while ! PGPASSWORD=${PG_REPLICATION_PASSWORD} "${backup_cmd}" "${backup_args[@]}";do
|
||
replication_counter=$(( replication_counter + 1 ))
|
||
LOG_D "Backup command failed ${replication_counter} times. Sleeping and trying again later[]"
|
||
sleep 5
|
||
done
|
||
}
|
||
|