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}