blob: eadd62c58b5b2439e1efae6ecdd340f9e7bb8f9b [file] [log] [blame]
#!/bin/bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
set -eo pipefail
shopt -s nullglob
# Constant Definition
readonly DORIS_HOME="/opt/apache-doris"
readonly MAX_RETRY_TIMES=60
readonly RETRY_INTERVAL=1
readonly MYSQL_PORT=9030
# Log Function
log_message() {
local level="$1"
shift
local message="$*"
if [ "$#" -eq 0 ]; then
message="$(cat)"
fi
local timestamp="$(date -Iseconds)"
printf '%s [%s] [Entrypoint]: %s\n' "${timestamp}" "${level}" "${message}"
}
log_info() {
log_message "INFO" "$@"
}
log_warn() {
log_message "WARN" "$@" >&2
}
log_error() {
log_message "ERROR" "$@" >&2
exit 1
}
# Check whether it is a source file call
is_sourced() {
[ "${#FUNCNAME[@]}" -ge 2 ] &&
[ "${FUNCNAME[0]}" = 'is_sourced' ] &&
[ "${FUNCNAME[1]}" = 'source' ]
}
# Verify IP address format
validate_ip_address() {
local ip="$1"
local ipv4_regex="^[1-2]?[0-9]?[0-9](\.[1-2]?[0-9]?[0-9]){3}$"
local ipv6_regex="^([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)$"
if [[ $ip =~ $ipv4_regex ]] || [[ $ip =~ $ipv6_regex ]]; then
return 0
fi
return 1
}
# Verify port number
validate_port() {
local port="$1"
if [[ $port =~ ^[1-6]?[0-9]{1,4}$ ]] && [ "$port" -le 65535 ]; then
return 0
fi
return 1
}
# Verify the necessary environment variables
validate_environment() {
declare -g run_mode
# Election Mode Verification
if [[ -n "$FE_SERVERS" && -n "$BE_ADDR" ]]; then
validate_election_mode
return
fi
# Specifying a schema for validation
if [[ -n "$FE_MASTER_IP" && -n "$BE_IP" && -n "$BE_PORT" ]]; then
validate_assign_mode
return
fi
log_error "Missing required parameters. Please check documentation."
}
# Verify election mode configuration
validate_election_mode() {
run_mode="ELECTION"
# Verify FE_SERVERS format
local fe_servers_regex="^.+:[1-2]{0,1}[0-9]{0,1}[0-9]{1}(\.[1-2]{0,1}[0-9]{0,1}[0-9]{1}){3}:[1-6]{0,1}[0-9]{1,4}(,.+:[1-2]{0,1}[0-9]{0,1}[0-9]{1}(\.[1-2]{0,1}[0-9]{0,1}[0-9]{1}){3}:[1-6]{0,1}[0-9]{1,4})*$"
if ! [[ $FE_SERVERS =~ $fe_servers_regex ]]; then
log_error "Invalid FE_SERVERS format. Expected: name:ip:port[,name:ip:port]..."
fi
# Verify BE_ADDR format
if ! validate_ip_address "$(echo "$BE_ADDR" | cut -d: -f1)" || \
! validate_port "$(echo "$BE_ADDR" | cut -d: -f2)"; then
log_error "Invalid BE_ADDR format. Expected: ip:port"
fi
log_info "Running in Election mode"
}
# Verify the specified mode configuration
validate_assign_mode() {
run_mode="ASSIGN"
# Verify IP Address
if ! validate_ip_address "$FE_MASTER_IP"; then
log_error "Invalid FE_MASTER_IP format"
fi
if ! validate_ip_address "$BE_IP"; then
log_error "Invalid BE_IP format"
fi
# Verify port
if ! validate_port "$BE_PORT"; then
log_error "Invalid BE_PORT"
fi
log_info "Running in Assign mode"
}
# Parsing configuration parameters
parse_config() {
declare -g MASTER_FE_IP CURRENT_BE_IP CURRENT_BE_PORT PRIORITY_NETWORKS
if [ "$run_mode" = "ELECTION" ]; then
# Analyze the main FE node information
MASTER_FE_IP=$(echo "$FE_SERVERS" | cut -d, -f1 | cut -d: -f2)
# Parsing BE node information
CURRENT_BE_IP=$(echo "$BE_ADDR" | cut -d: -f1)
CURRENT_BE_PORT=$(echo "$BE_ADDR" | cut -d: -f2)
else
MASTER_FE_IP="$FE_MASTER_IP"
CURRENT_BE_IP="$BE_IP"
CURRENT_BE_PORT="$BE_PORT"
fi
# Set up a preferred network
PRIORITY_NETWORKS=$(echo "$CURRENT_BE_IP" | awk -F. '{print $1"."$2"."$3".0/24"}')
# Exporting environment variables
export MASTER_FE_IP CURRENT_BE_IP CURRENT_BE_PORT PRIORITY_NETWORKS
}
# Check BE status
check_be_status() {
local retry_count=0
while [ $retry_count -lt $MAX_RETRY_TIMES ]; do
if [ "$1" = "true" ]; then
# Check FE status
if mysql -uroot -P"${MYSQL_PORT}" -h"${MASTER_FE_IP}" \
-N -e "SHOW FRONTENDS" 2>/dev/null | grep -w "${MASTER_FE_IP}" &>/dev/null; then
log_info "Master FE is ready"
return 0
fi
else
# Check BE status
if mysql -uroot -P"${MYSQL_PORT}" -h"${MASTER_FE_IP}" \
-N -e "SHOW BACKENDS" 2>/dev/null | grep -w "${CURRENT_BE_IP}" | grep -w "${CURRENT_BE_PORT}" | grep -w "true" &>/dev/null; then
log_info "BE node is ready"
return 0
fi
fi
retry_count=$((retry_count + 1))
if [ $((retry_count % 20)) -eq 1 ]; then
if [ "$1" = "true" ]; then
log_info "Waiting for master FE... ($retry_count/$MAX_RETRY_TIMES)"
else
log_info "Waiting for BE node... ($retry_count/$MAX_RETRY_TIMES)"
fi
fi
sleep "$RETRY_INTERVAL"
done
return 1
}
# Processing initialization files
process_init_files() {
local f
for f; do
case "$f" in
*.sh)
if [ -x "$f" ]; then
log_info "Executing $f"
"$f"
else
log_info "Sourcing $f"
. "$f"
fi
;;
*.sql)
log_info "Executing SQL file $f"
mysql -uroot -P"${MYSQL_PORT}" -h"${MASTER_FE_IP}" < "$f"
;;
*.sql.gz)
log_info "Executing compressed SQL file $f"
gunzip -c "$f" | mysql -uroot -P"${MYSQL_PORT}" -h"${MASTER_FE_IP}"
;;
*)
log_warn "Ignoring $f"
;;
esac
done
}
# Main Function
main() {
validate_environment
parse_config
# Start BE Node
{
set +e
bash init_be.sh 2>/dev/null
} &
# Waiting for BE node to be ready
if ! check_be_status false; then
log_error "BE node failed to start"
fi
# Processing initialization files
if [ -d "/docker-entrypoint-initdb.d" ]; then
sleep 15 # Wait for the system to fully boot up
process_init_files /docker-entrypoint-initdb.d/*
fi
# Waiting for BE process
wait
}
if ! is_sourced; then
main "$@"
fi