github.com/percona/percona-xtradb-cluster-operator@v1.14.0/build/pxc-entrypoint.sh (about) 1 #!/bin/bash 2 set -eo pipefail 3 shopt -s nullglob 4 set -o xtrace 5 6 trap "exit" SIGTERM 7 8 # if command starts with an option, prepend mysqld 9 if [ "${1:0:1}" = '-' ]; then 10 set -- mysqld "$@" 11 fi 12 CFG=/etc/mysql/node.cnf 13 14 # skip setup if they want an option that stops mysqld 15 wantHelp= 16 for arg; do 17 case "$arg" in 18 -'?' | --help | --print-defaults | -V | --version) 19 wantHelp=1 20 break 21 ;; 22 esac 23 done 24 25 # usage: file_env VAR [DEFAULT] 26 # ie: file_env 'XYZ_DB_PASSWORD' 'example' 27 # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of 28 # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) 29 file_env() { 30 { set +x; } 2>/dev/null 31 local var="$1" 32 local fileVar="${var}_FILE" 33 local def="${2:-}" 34 if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then 35 echo >&2 "error: both $var and $fileVar are set (but are exclusive)" 36 exit 1 37 fi 38 local val="$def" 39 if [ "${!var:-}" ]; then 40 val="${!var}" 41 elif [ "${!fileVar:-}" ]; then 42 val="$(<"${!fileVar}")" 43 elif [ "${3:-}" ] && [ -f "/etc/mysql/mysql-users-secret/$3" ]; then 44 val="$(</etc/mysql/mysql-users-secret/$3)" 45 fi 46 export "$var"="$val" 47 unset "$fileVar" 48 } 49 50 # usage: process_init_file FILENAME MYSQLCOMMAND... 51 # ie: process_init_file foo.sh mysql -uroot 52 # (process a single initializer file, based on its extension. we define this 53 # function here, so that initializer scripts (*.sh) can use the same logic, 54 # potentially recursively, or override the logic used in subsequent calls) 55 process_init_file() { 56 local f="$1" 57 shift 58 local mysql=("$@") 59 60 case "$f" in 61 *.sh) 62 echo "$0: running $f" 63 . "$f" 64 ;; 65 *.sql) 66 echo "$0: running $f" 67 "${mysql[@]}" <"$f" 68 echo 69 ;; 70 *.sql.gz) 71 echo "$0: running $f" 72 gunzip -c "$f" | "${mysql[@]}" 73 echo 74 ;; 75 *) echo "$0: ignoring $f" ;; 76 esac 77 echo 78 } 79 80 _check_config() { 81 toRun=("$@" --verbose --help --wsrep-provider='none') 82 if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then 83 cat >&2 <<-EOM 84 85 ERROR: mysqld failed while attempting to check config 86 command was: "${toRun[*]}" 87 88 $errors 89 EOM 90 exit 1 91 fi 92 } 93 94 # Fetch value from server config 95 # We use mysqld --verbose --help instead of my_print_defaults because the 96 # latter only show values present in config files, and not server defaults 97 _get_config() { 98 local conf="$1" 99 shift 100 "$@" --verbose --help --wsrep-provider='none' --log-bin-index="$(mktemp -u)" 2>/dev/null \ 101 | awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' 102 # match "datadir /some/path with/spaces in/it here" but not "--xyz=abc\n datadir (xyz)" 103 } 104 105 # Fetch value from customized configs, needed for non-mysqld options like sst 106 _get_cnf_config() { 107 local group=$1 108 local var=${2//_/-} 109 local reval="" 110 111 reval=$( 112 my_print_defaults "${group}" \ 113 | awk -F= '{st=index($0,"="); cur=$0; if ($1 ~ /_/) { gsub(/_/,"-",$1);} if (st != 0) { print $1"="substr(cur,st+1) } else { print cur }}' \ 114 | grep -- "--$var=" \ 115 | cut -d= -f2- \ 116 | tail -1 117 ) 118 119 if [[ -z $reval ]]; then 120 reval=$3 121 fi 122 echo "$reval" 123 } 124 125 # _get_tmpdir return temporary dir similar to 'initialize_tmpdir' function 126 # and $JOINER_SST_DIR selection logic inside 'wsrep_sst_xtrabackup-v2.sh' 127 _get_tmpdir() { 128 local defaul_value="$1" 129 local tmpdir_path="" 130 131 tmpdir_path=$(_get_cnf_config sst tmpdir "") 132 if [[ -z ${tmpdir_path} ]]; then 133 tmpdir_path=$(_get_cnf_config xtrabackup tmpdir "") 134 fi 135 if [[ -z ${tmpdir_path} ]]; then 136 tmpdir_path=$(_get_cnf_config mysqld tmpdir "") 137 fi 138 if [[ -z ${tmpdir_path} ]]; then 139 tmpdir_path="$defaul_value" 140 fi 141 echo "$tmpdir_path" 142 } 143 144 function join { 145 local IFS="$1" 146 shift 147 joined=$(tr "$IFS" '\n' <<<"$*" | sort -u | tr '\n' "$IFS") 148 echo "${joined%?}" 149 } 150 151 escape_special() { 152 { set +x; } 2>/dev/null 153 echo "$1" \ 154 | sed 's/\\/\\\\/g' \ 155 | sed 's/'\''/'\\\\\''/g' \ 156 | sed 's/"/\\\"/g' 157 } 158 159 MYSQL_VERSION=$(mysqld -V | awk '{print $3}' | awk -F'.' '{print $1"."$2}') 160 MYSQL_PATCH_VERSION=$(mysqld -V | awk '{print $3}' | awk -F'.' '{print $3}' | awk -F'-' '{print $1}') 161 162 # if vault secret file exists we assume we need to turn on encryption 163 vault_secret="/etc/mysql/vault-keyring-secret/keyring_vault.conf" 164 if [ -f "$vault_secret" ]; then 165 sed -i "/\[mysqld\]/a early-plugin-load=keyring_vault.so" $CFG 166 sed -i "/\[mysqld\]/a keyring_vault_config=$vault_secret" $CFG 167 168 if [ "$MYSQL_VERSION" == '8.0' ]; then 169 sed -i "/\[mysqld\]/a default_table_encryption=ON" $CFG 170 sed -i "/\[mysqld\]/a table_encryption_privilege_check=ON" $CFG 171 sed -i "/\[mysqld\]/a innodb_undo_log_encrypt=ON" $CFG 172 sed -i "/\[mysqld\]/a innodb_redo_log_encrypt=ON" $CFG 173 sed -i "/\[mysqld\]/a binlog_encryption=ON" $CFG 174 sed -i "/\[mysqld\]/a binlog_rotate_encryption_master_key_at_startup=ON" $CFG 175 sed -i "/\[mysqld\]/a innodb_temp_tablespace_encrypt=ON" $CFG 176 sed -i "/\[mysqld\]/a innodb_parallel_dblwr_encrypt=ON" $CFG 177 sed -i "/\[mysqld\]/a innodb_encrypt_online_alter_logs=ON" $CFG 178 sed -i "/\[mysqld\]/a encrypt_tmp_files=ON" $CFG 179 fi 180 fi 181 182 if [ -f "/usr/lib64/mysql/plugin/binlog_utils_udf.so" ]; then 183 sed -i '/\[mysqld\]/a plugin_load="binlog_utils_udf=binlog_utils_udf.so"' $CFG 184 sed -i "/\[mysqld\]/a gtid-mode=ON" $CFG 185 sed -i "/\[mysqld\]/a enforce-gtid-consistency" $CFG 186 fi 187 188 # add sst.cpat to exclude pxc-entrypoint, unsafe-bootstrap, pxc-configure-pxc from SST cleanup 189 grep -q "^progress=" $CFG && sed -i "s|^progress=.*|progress=1|" $CFG 190 grep -q "^\[sst\]" "$CFG" || printf '[sst]\n' >>"$CFG" 191 grep -q "^cpat=" "$CFG" || sed '/^\[sst\]/a cpat=.*\\.pem$\\|.*init\\.ok$\\|.*galera\\.cache$\\|.*wsrep_recovery_verbose\\.log$\\|.*readiness-check\\.sh$\\|.*liveness-check\\.sh$\\|.*get-pxc-state$\\|.*sst_in_progress$\\|.*sleep-forever$\\|.*pmm-prerun\\.sh$\\|.*sst-xb-tmpdir$\\|.*\\.sst$\\|.*gvwstate\\.dat$\\|.*grastate\\.dat$\\|.*\\.err$\\|.*\\.log$\\|.*RPM_UPGRADE_MARKER$\\|.*RPM_UPGRADE_HISTORY$\\|.*pxc-entrypoint\\.sh$\\|.*unsafe-bootstrap\\.sh$\\|.*pxc-configure-pxc\\.sh\\|.*peer-list$\\|.*auth_plugin$\\|.*version_info$' "$CFG" 1<>"$CFG" 192 if [[ $MYSQL_VERSION == '8.0' ]]; then 193 if [[ $MYSQL_PATCH_VERSION -ge 26 ]]; then 194 grep -q "^skip_replica_start=ON" "$CFG" || sed -i "/\[mysqld\]/a skip_replica_start=ON" $CFG 195 else 196 grep -q "^skip_slave_start=ON" "$CFG" || sed -i "/\[mysqld\]/a skip_slave_start=ON" $CFG 197 fi 198 fi 199 200 auth_plugin=${DEFAULT_AUTHENTICATION_PLUGIN} 201 if [[ -f /var/lib/mysql/auth_plugin ]]; then 202 prev_auth_plugin=$(cat /var/lib/mysql/auth_plugin) 203 if [[ ${prev_auth_plugin} != "mysql_native_password" && ${auth_plugin} == "mysql_native_password" ]]; then 204 echo "FATAL: It's forbidden to switch from ${prev_auth_plugin} to ${auth_plugin}." 205 echo "If ProxySQL is enabled operator uses mysql_native_password since it doesn't work with caching_sha2_password." 206 echo "Using caching_sha2_password will break frontend connections in ProxySQL." 207 echo "You can remove /var/lib/mysql/auth_plugin to force the switch." 208 echo "Please check K8SPXC-1183 for more information." 209 exit 1 210 fi 211 fi 212 213 if [[ -z ${auth_plugin} ]]; then 214 auth_plugin="mysql_native_password" 215 elif [[ $MYSQL_VERSION == '5.7' ]]; then 216 auth_plugin="mysql_native_password" 217 fi 218 219 echo "${auth_plugin}" >/var/lib/mysql/auth_plugin 220 221 sed -i "/default_authentication_plugin/d" $CFG 222 if [[ $MYSQL_VERSION == '8.0' && $MYSQL_PATCH_VERSION -ge 27 ]]; then 223 sed -i "/\[mysqld\]/a authentication_policy=${auth_plugin},," $CFG 224 else 225 sed -i "/\[mysqld\]/a default_authentication_plugin=${auth_plugin}" $CFG 226 fi 227 228 file_env 'XTRABACKUP_PASSWORD' 'xtrabackup' 'xtrabackup' 229 230 NODE_NAME=$(hostname -f) 231 NODE_PORT=3306 232 # Is running in Kubernetes/OpenShift, so find all other pods belonging to the cluster 233 if [ -n "$PXC_SERVICE" ]; then 234 echo "Percona XtraDB Cluster: Finding peers" 235 /var/lib/mysql/peer-list -on-start="/var/lib/mysql/pxc-configure-pxc.sh" -service="${PXC_SERVICE}" 236 CLUSTER_JOIN="$(grep '^wsrep_cluster_address=' "$CFG" | cut -d '=' -f 2 | sed -e 's^.*gcomm://^^')" 237 echo "Cluster address set to: $CLUSTER_JOIN" 238 elif [ -n "$DISCOVERY_SERVICE" ]; then 239 echo 'Registering in the discovery service' 240 NODE_IP=$(hostname -I | awk ' { print $1 } ') 241 242 if [ "${DISCOVERY_SERVICE:0:4}" != "http" ]; then 243 DISCOVERY_SERVICE="http://${DISCOVERY_SERVICE}" 244 fi 245 curl "$DISCOVERY_SERVICE/v2/keys/pxc-cluster/queue/$CLUSTER_NAME" -XPOST -d value="$NODE_IP" -d ttl=60 246 247 #get list of IP from queue 248 i=$(curl "$DISCOVERY_SERVICE/v2/keys/pxc-cluster/queue/$CLUSTER_NAME" | jq -r '.node.nodes[].value') 249 250 # this remove my ip from the list 251 i1="${i[@]//$NODE_IP/}" 252 253 # Register the current IP in the discovery service 254 # key set to expire in 30 sec. There is a cronjob that should update them regularly 255 curl "$DISCOVERY_SERVICE/v2/keys/pxc-cluster/$CLUSTER_NAME/$NODE_IP/ipaddr" -XPUT -d value="$NODE_IP" -d ttl=30 256 curl "$DISCOVERY_SERVICE/v2/keys/pxc-cluster/$CLUSTER_NAME/$NODE_IP/hostname" -XPUT -d value="$HOSTNAME" -d ttl=30 257 curl "$DISCOVERY_SERVICE/v2/keys/pxc-cluster/$CLUSTER_NAME/$NODE_IP" -XPUT -d ttl=30 -d dir=true -d prevExist=true 258 259 i=$(curl "$DISCOVERY_SERVICE/v2/keys/pxc-cluster/$CLUSTER_NAME/?quorum=true" | jq -r '.node.nodes[]?.key' | awk -F'/' '{print $(NF)}') 260 # this remove my ip from the list 261 i2="${i[@]//$NODE_IP/}" 262 CLUSTER_JOIN=$(join , $i1 $i2) 263 264 sed -r "s|^[#]?wsrep_node_address=.*$|wsrep_node_address=${NODE_IP}|" "${CFG}" 1<>"${CFG}" 265 sed -r "s|^[#]?wsrep_cluster_name=.*$|wsrep_cluster_name=${CLUSTER_NAME}|" "${CFG}" 1<>"${CFG}" 266 sed -r "s|^[#]?wsrep_cluster_address=.*$|wsrep_cluster_address=gcomm://${CLUSTER_JOIN}|" "${CFG}" 1<>"${CFG}" 267 sed -r "s|^[#]?wsrep_node_incoming_address=.*$|wsrep_node_incoming_address=${NODE_NAME}:${NODE_PORT}|" "${CFG}" 1<>"${CFG}" 268 { set +x; } 2>/dev/null 269 sed -r "s|^[#]?wsrep_sst_auth=.*$|wsrep_sst_auth='xtrabackup:${XTRABACKUP_PASSWORD}'|" "${CFG}" 1<>"${CFG}" 270 271 else 272 : checking incoming cluster parameters 273 NODE_IP=$(hostname -I | awk ' { print $1 } ') 274 sed -r "s|^[#]?wsrep_node_address=.*$|wsrep_node_address=${NODE_IP}|" "${CFG}" 1<>"${CFG}" 275 sed -r "s|^[#]?wsrep_node_incoming_address=.*$|wsrep_node_incoming_address=${NODE_NAME}:${NODE_PORT}|" "${CFG}" 1<>"${CFG}" 276 { set +x; } 2>/dev/null 277 sed -r "s|^[#]?wsrep_sst_auth=.*$|wsrep_sst_auth='xtrabackup:${XTRABACKUP_PASSWORD}'|" "${CFG}" 1<>"${CFG}" 278 set -x 279 280 if [[ -n ${CLUSTER_JOIN} ]]; then 281 sed -r "s|^[#]?wsrep_cluster_address=.*$|wsrep_cluster_address=gcomm://${CLUSTER_JOIN}|" "${CFG}" 1<>"${CFG}" 282 fi 283 284 if [[ -n ${CLUSTER_NAME} ]]; then 285 sed -r "s|^[#]?wsrep_cluster_name=.*$|wsrep_cluster_name=${CLUSTER_NAME}|" "${CFG}" 1<>"${CFG}" 286 fi 287 288 fi 289 290 WSREP_CLUSTER_NAME=$(grep wsrep_cluster_name ${CFG} | cut -d '=' -f 2 | tr -d ' ') 291 if [[ -z ${WSREP_CLUSTER_NAME} || ${WSREP_CLUSTER_NAME} == 'noname' ]]; then 292 echo "Cluster name is invalid, please check DNS" 293 exit 1 294 fi 295 296 # if we have CLUSTER_JOIN - then we do not need to perform datadir initialize 297 # the data will be copied from another node 298 299 if [ -z "$CLUSTER_JOIN" ] && [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then 300 # still need to check config, container may have started with --user 301 _check_config "$@" 302 303 # Get config 304 DATADIR="$(_get_config 'datadir' "$@")" 305 TMPDIR=$(_get_tmpdir "$DATADIR/sst-xb-tmpdir") 306 307 rm -rfv "$TMPDIR" 308 309 if [ ! -d "$DATADIR/mysql" ]; then 310 file_env 'MYSQL_ROOT_PASSWORD' '' 'root' 311 { set +x; } 2>/dev/null 312 if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then 313 echo >&2 'error: database is uninitialized and password option is not specified ' 314 echo >&2 ' You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD' 315 exit 1 316 fi 317 set -x 318 319 mkdir -p "$DATADIR" 320 cpat="$(_get_cnf_config sst cpat)" 321 find "$DATADIR" -mindepth 1 -regex "$cpat" -prune -o -exec rm -rfv {} \+ 1>/dev/null 322 323 echo 'Initializing database' 324 # we initialize database into $TMPDIR because "--initialize-insecure" option does not work if directory is not empty 325 # in some cases storage driver creates unremovable artifacts (see K8SPXC-286), so $DATADIR cleanup is not possible 326 "$@" --initialize-insecure --skip-ssl --datadir="$TMPDIR" 327 mv "$TMPDIR"/* "$DATADIR/" 328 rm -rfv "$TMPDIR" 329 echo 'Database initialized' 330 331 echo "${auth_plugin}" >/var/lib/mysql/auth_plugin 332 333 SOCKET="$(_get_config 'socket' "$@")" 334 "$@" --skip-networking --socket="${SOCKET}" & 335 pid="$!" 336 337 mysql=(mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" --password="") 338 wsrep_local_state_select="SELECT variable_value FROM performance_schema.global_status WHERE variable_name='wsrep_local_state_comment'" 339 340 for i in {120..0}; do 341 wsrep_local_state=$(echo "$wsrep_local_state_select" | "${mysql[@]}" -s 2>/dev/null) || true 342 if [ "$wsrep_local_state" = 'Synced' ]; then 343 break 344 fi 345 echo 'MySQL init process in progress...' 346 sleep 1 347 done 348 if [ "$i" = 0 ]; then 349 echo >&2 'MySQL init process failed.' 350 exit 1 351 fi 352 353 if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then 354 ( 355 echo "set wsrep_on=0;" 356 echo "SET @@SESSION.SQL_LOG_BIN = off;" 357 # sed is for https://bugs.mysql.com/bug.php?id=20545 358 mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' 359 echo "set wsrep_on=1;" 360 ) | "${mysql[@]}" mysql 361 fi 362 363 { set +x; } 2>/dev/null 364 if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then 365 MYSQL_ROOT_PASSWORD="$(pwmake 128)" 366 echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" 367 fi 368 369 rootCreate= 370 # default root to listen for connections from anywhere 371 file_env 'MYSQL_ROOT_HOST' '%' 372 if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then 373 # no, we don't care if read finds a terminating character in this heredoc 374 # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 375 read -r -d '' rootCreate <<-EOSQL || true 376 CREATE USER 'root'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '$(escape_special "${MYSQL_ROOT_PASSWORD}")' PASSWORD EXPIRE NEVER; 377 GRANT ALL ON *.* TO 'root'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION ; 378 EOSQL 379 fi 380 381 file_env 'MONITOR_HOST' 'localhost' 382 file_env 'MONITOR_PASSWORD' 'monitor' 'monitor' 383 file_env 'REPLICATION_PASSWORD' 'replication' 'replication' 384 if [ "$MYSQL_VERSION" == '8.0' ]; then 385 read -r -d '' monitorConnectGrant <<-EOSQL || true 386 GRANT SERVICE_CONNECTION_ADMIN ON *.* TO 'monitor'@'${MONITOR_HOST}'; 387 EOSQL 388 fi 389 390 # SYSTEM_USER since 8.0.16 391 # https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_system-user 392 if [[ $MYSQL_VERSION == "8.0" ]] && ((MYSQL_PATCH_VERSION >= 16)); then 393 read -r -d '' systemUserGrant <<-EOSQL || true 394 GRANT SYSTEM_USER ON *.* TO 'monitor'@'${MONITOR_HOST}'; 395 EOSQL 396 fi 397 398 "${mysql[@]}" <<-EOSQL 399 -- What's done in this file shouldn't be replicated 400 -- or products like mysql-fabric won't work 401 SET @@SESSION.SQL_LOG_BIN=0; 402 403 DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mysqlxsys', 'root', 'mysql.infoschema', 'mysql.pxc.internal.session', 'mysql.pxc.sst.role', 'mysql.session') OR host NOT IN ('localhost') ; 404 ALTER USER 'root'@'localhost' IDENTIFIED BY '$(escape_special "${MYSQL_ROOT_PASSWORD}")' ; 405 GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ; 406 ${rootCreate} 407 /*!80016 REVOKE SYSTEM_USER ON *.* FROM root */; 408 409 CREATE USER 'operator'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '$(escape_special "${OPERATOR_ADMIN_PASSWORD}")' PASSWORD EXPIRE NEVER; 410 GRANT ALL ON *.* TO 'operator'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION ; 411 412 CREATE USER 'xtrabackup'@'%' IDENTIFIED BY '$(escape_special "${XTRABACKUP_PASSWORD}")' PASSWORD EXPIRE NEVER; 413 GRANT ALL ON *.* TO 'xtrabackup'@'%'; 414 415 CREATE USER 'monitor'@'${MONITOR_HOST}' IDENTIFIED BY '$(escape_special "${MONITOR_PASSWORD}")' WITH MAX_USER_CONNECTIONS 100 PASSWORD EXPIRE NEVER; 416 GRANT SELECT, PROCESS, SUPER, REPLICATION CLIENT, RELOAD ON *.* TO 'monitor'@'${MONITOR_HOST}'; 417 GRANT SELECT ON performance_schema.* TO 'monitor'@'${MONITOR_HOST}'; 418 ${monitorConnectGrant} 419 420 ${systemUserGrant} 421 422 CREATE USER 'replication'@'%' IDENTIFIED BY '$(escape_special "${REPLICATION_PASSWORD}")' PASSWORD EXPIRE NEVER; 423 GRANT REPLICATION SLAVE ON *.* to 'replication'@'%'; 424 DROP DATABASE IF EXISTS test; 425 FLUSH PRIVILEGES ; 426 EOSQL 427 428 { set +x; } 2>/dev/null 429 if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then 430 mysql+=(-p"${MYSQL_ROOT_PASSWORD}") 431 fi 432 set -x 433 434 file_env 'MYSQL_DATABASE' 435 if [ "$MYSQL_DATABASE" ]; then 436 echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}" 437 mysql+=("$MYSQL_DATABASE") 438 fi 439 440 file_env 'MYSQL_USER' 441 file_env 'MYSQL_PASSWORD' 442 { set +x; } 2>/dev/null 443 if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then 444 echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}" 445 446 if [ "$MYSQL_DATABASE" ]; then 447 echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}" 448 fi 449 450 echo 'FLUSH PRIVILEGES ;' | "${mysql[@]}" 451 fi 452 set -x 453 454 echo 455 ls /docker-entrypoint-initdb.d/ >/dev/null 456 for f in /docker-entrypoint-initdb.d/*; do 457 process_init_file "$f" "${mysql[@]}" 458 done 459 460 { set +x; } 2>/dev/null 461 if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then 462 "${mysql[@]}" <<-EOSQL 463 ALTER USER 'root'@'%' PASSWORD EXPIRE; 464 EOSQL 465 fi 466 set -x 467 if ! kill -s TERM "$pid" || ! wait "$pid"; then 468 echo >&2 'MySQL init process failed.' 469 exit 1 470 fi 471 472 echo 473 echo 'MySQL init process done. Ready for start up.' 474 echo 475 fi 476 477 # exit when MYSQL_INIT_ONLY environment variable is set to avoid starting mysqld 478 if [ ! -z "$MYSQL_INIT_ONLY" ]; then 479 echo 'Initialization complete, now exiting!' 480 exit 0 481 fi 482 fi 483 484 if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then 485 # still need to check config, container may have started with --user 486 _check_config "$@" 487 488 DATADIR=$(_get_config 'datadir' "$@") 489 SST_DIR=$(_get_cnf_config sst tmpdir "${DATADIR}/sst-xb-tmpdir") 490 SST_P_FILE=$(_get_cnf_config sst progress "${DATADIR}/sst_in_progress") 491 rm -rvf "${SST_DIR}" "${SST_P_FILE}" 492 493 "$@" --version | sed 's/-ps//' | awk '{print $3}' | tee /tmp/version_info 494 495 pxc_version_file='' 496 pxc_version='' 497 if [ -f "$DATADIR/version_info" ]; then 498 pxc_version_file="$DATADIR/version_info" 499 pxc_version=$(cat $pxc_version_file | awk '{print $3}') 500 elif [ -f "$DATADIR/xtrabackup_info" ]; then 501 pxc_version_file="$DATADIR/xtrabackup_info" 502 pxc_version=$(grep 'server_version' $pxc_version_file | awk '{print $3}' | tr -d '\n') 503 fi 504 505 506 if [[ -f $pxc_version_file && -n $pxc_version && $MYSQL_VERSION == '5.7' ]] && [[ $(cat /tmp/version_info) != $pxc_version ]]; then 507 SOCKET="$(_get_config 'socket' "$@")" 508 "$@" --skip-networking --socket="${SOCKET}" --wsrep-provider='none' & 509 pid="$!" 510 511 mysql=(mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" --password="") 512 { set +x; } 2>/dev/null 513 if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then 514 mysql+=(-p"${MYSQL_ROOT_PASSWORD}") 515 fi 516 set -x 517 518 for i in {120..0}; do 519 if echo 'SELECT 1' | "${mysql[@]}" &>/dev/null; then 520 break 521 fi 522 echo 'MySQL init process in progress...' 523 sleep 1 524 done 525 if [ "$i" = 0 ]; then 526 echo >&2 'MySQL init process failed.' 527 exit 1 528 fi 529 530 mysql_upgrade --force "${mysql[@]:1}" 531 if ! kill -s TERM "$pid" || ! wait "$pid"; then 532 echo >&2 'MySQL init process failed.' 533 exit 1 534 fi 535 fi 536 "$@" --version | sed 's/-ps//' >"$DATADIR/version_info" 537 grep -v wsrep_sst_auth "$CFG" 538 fi 539 540 POD_NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace) 541 542 wsrep_start_position_opt="" 543 if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then 544 DATADIR="$(_get_config 'datadir' "$@")" 545 grastate_loc="${DATADIR}/grastate.dat" 546 wsrep_verbose_logfile="$DATADIR/wsrep_recovery_verbose.log" 547 if [ -f "$wsrep_verbose_logfile" ]; then 548 cat "$wsrep_verbose_logfile" | tee -a "$DATADIR/wsrep_recovery_verbose_history.log" 549 rm -f "$wsrep_verbose_logfile" 550 fi 551 552 if [ -s "$grastate_loc" -a -d "$DATADIR/mysql" ]; then 553 uuid=$(grep 'uuid:' "$grastate_loc" | cut -d: -f2 | tr -d ' ' || :) 554 seqno=$(grep 'seqno:' "$grastate_loc" | cut -d: -f2 | tr -d ' ' || :) 555 safe_to_bootstrap=$(grep 'safe_to_bootstrap:' "$grastate_loc" | cut -d: -f2 | tr -d ' ' || :) 556 557 # If sequence number is not equal to -1, wsrep-recover co-ordinates aren't used. 558 # lp:1112724 559 # So, directly pass whatever is obtained from grastate.dat 560 if [ -n "$seqno" ] && [ "$seqno" -ne -1 ]; then 561 echo "Skipping wsrep-recover for $uuid:$seqno pair" 562 echo "Assigning $uuid:$seqno to wsrep_start_position" 563 wsrep_start_position_opt="--wsrep_start_position=$uuid:$seqno" 564 fi 565 fi 566 567 if [ -z "$wsrep_start_position_opt" -a -d "$DATADIR/mysql" ]; then 568 "$@" --wsrep_recover --log-error-verbosity=3 --log_error="$wsrep_verbose_logfile" 569 570 echo >&2 "WSREP: Print recovery logs: " 571 cat "$wsrep_verbose_logfile" | tee -a "$DATADIR/wsrep_recovery_verbose_history.log" 572 if grep ' Recovered position:' "$wsrep_verbose_logfile"; then 573 start_pos="$( 574 grep ' Recovered position:' "$wsrep_verbose_logfile" \ 575 | sed 's/.*\ Recovered\ position://' \ 576 | sed 's/^[ \t]*//' 577 )" 578 wsrep_start_position_opt="--wsrep_start_position=$start_pos" 579 seqno=$(echo "$start_pos" | awk -F':' '{print $NF}' || :) 580 else 581 # The server prints "..skipping position recovery.." if started without wsrep. 582 if grep 'skipping position recovery' "$wsrep_verbose_logfile"; then 583 echo "WSREP: Position recovery skipped" 584 else 585 echo >&2 "WSREP: Failed to recover position: " 586 exit 1 587 fi 588 fi 589 rm "$wsrep_verbose_logfile" 590 fi 591 if [ -n "$PXC_SERVICE" ]; then 592 function get_primary() { 593 /var/lib/mysql/peer-list -on-start=/var/lib/mysql/get-pxc-state -service="$PXC_SERVICE" 2>&1 \ 594 | grep wsrep_ready:ON:wsrep_connected:ON:wsrep_local_state_comment:Synced:wsrep_cluster_status:Primary \ 595 | sort \ 596 | tail -1 \ 597 || true 598 } 599 function node_recovery() { 600 set -o xtrace 601 echo "Recovery is in progress, please wait...." 602 sed -i 's/wsrep_cluster_address=.*/wsrep_cluster_address=gcomm:\/\//g' /etc/mysql/node.cnf 603 rm -f /tmp/recovery-case 604 if [ -s "$grastate_loc" ]; then 605 sed -i 's/safe_to_bootstrap: 0/safe_to_bootstrap: 1/g' "$grastate_loc" 606 fi 607 echo "Recovery was finished." 608 exec "$@" $wsrep_start_position_opt 609 } 610 function is_manual_recovery() { 611 set +o xtrace 612 recovery_file='/var/lib/mysql/sleep-forever' 613 if [ -f "${recovery_file}" ]; then 614 echo "The $recovery_file file is detected, node is going to infinity loop" 615 echo "If you want to exit from infinity loop you need to remove $recovery_file file" 616 for (( ; ; )); do 617 if [ ! -f "${recovery_file}" ]; then 618 exit 0 619 fi 620 done 621 fi 622 set -o xtrace 623 } 624 625 is_primary_exists=$(get_primary) 626 is_manual_recovery 627 if [[ -z $is_primary_exists && -f $grastate_loc && $safe_to_bootstrap != 1 ]] \ 628 || [[ -z $is_primary_exists && -f "${DATADIR}/gvwstate.dat" ]] \ 629 || [[ -z $is_primary_exists && -f $grastate_loc && $safe_to_bootstrap == 1 && -n ${CLUSTER_JOIN} ]]; then 630 trap '{ node_recovery "$@" ; }' USR1 631 touch /tmp/recovery-case 632 if [[ -z ${seqno} ]]; then 633 seqno="-1" 634 fi 635 636 set +o xtrace 637 sleep 3 638 639 echo "#####################################################FULL_PXC_CLUSTER_CRASH:$NODE_NAME#####################################################" 640 echo 'You have the situation of a full PXC cluster crash. In order to restore your PXC cluster, please check the log' 641 echo 'from all pods/nodes to find the node with the most recent data (the one with the highest sequence number (seqno).' 642 echo "It is $NODE_NAME node with sequence number (seqno): $seqno" 643 echo 'Cluster will recover automatically from the crash now.' 644 echo 'If you have set spec.pxc.autoRecovery to false, run the following command to recover manually from this node:' 645 echo "kubectl -n $POD_NAMESPACE exec $(hostname) -c pxc -- sh -c 'kill -s USR1 1'" 646 #DO NOT CHANGE THE LINE BELOW. OUR AUTO-RECOVERY IS USING IT TO DETECT SEQNO OF CURRENT NODE. See K8SPXC-564 647 echo "#####################################################LAST_LINE:$NODE_NAME:$seqno:#####################################################" 648 649 for (( ; ; )); do 650 is_primary_exists=$(get_primary) 651 if [ -n "$is_primary_exists" ]; then 652 rm -f /tmp/recovery-case 653 exit 0 654 fi 655 done 656 set -o xtrace 657 fi 658 fi 659 fi 660 661 test -e /opt/percona/hookscript/hook.sh && source /opt/percona/hookscript/hook.sh 662 663 init_opt="" 664 if [[ -f /etc/mysql/init-file/init.sql ]]; then 665 init_opt="--init-file=/etc/mysql/init-file/init.sql" 666 echo "Using init-file: /etc/mysql/init-file/init.sql" 667 fi 668 669 exec "$@" ${wsrep_start_position_opt} ${init_opt}