Files
containers/bitnami/clickhouse/22.3/debian-11/rootfs/opt/bitnami/scripts/libclickhouse.sh
Bitnami Bot ea4a405046 [bitnami/clickhouse] Release 22.3.12-debian-11-r0 (#6617)
Signed-off-by: Bitnami Containers <bitnami-bot@vmware.com>

Signed-off-by: Bitnami Containers <bitnami-bot@vmware.com>
Signed-off-by: Javier Salmeron Garcia <jsalmeron@vmware.com>
Co-authored-by: Javier Salmeron Garcia <jsalmeron@vmware.com>
2022-09-15 14:02:22 +00:00

276 lines
9.6 KiB
Bash

#!/bin/bash
#
# Bitnami ClickHouse library
# shellcheck disable=SC1090
# shellcheck disable=SC1091
# Load generic libraries
. /opt/bitnami/scripts/libfs.sh
. /opt/bitnami/scripts/libos.sh
. /opt/bitnami/scripts/libnet.sh
. /opt/bitnami/scripts/libfile.sh
. /opt/bitnami/scripts/libvalidations.sh
. /opt/bitnami/scripts/libservice.sh
########################
# Validate settings in CLICKHOUSE_* env vars
# Globals:
# CLICKHOUSE_*
# Arguments:
# None
# Returns:
# 0 if the validation succeeded, 1 otherwise
#########################
clickhouse_validate() {
debug "Validating settings in CLICKHOUSE_* environment variables..."
local error_code=0
# Auxiliary functions
print_validation_error() {
error "$1"
error_code=1
}
check_empty_value() {
if is_empty_value "${!1}"; then
print_validation_error "${1} must be set"
fi
}
check_valid_port() {
local port_var="${1:?missing port variable}"
local err
if ! err="$(validate_port "${!port_var}")"; then
print_validation_error "An invalid port was specified in the environment variable ${port_var}: ${err}."
fi
}
# Validate user inputs
! is_empty_value "$CLICKHOUSE_HTTP_PORT" && check_valid_port "CLICKHOUSE_HTTP_PORT"
! is_empty_value "$CLICKHOUSE_TCP_PORT" && check_valid_port "CLICKHOUSE_TCP_PORT"
! is_empty_value "$CLICKHOUSE_MYSQL_PORT" && check_valid_port "CLICKHOUSE_MYSQL_PORT"
! is_empty_value "$CLICKHOUSE_POSTGRESQL_PORT" && check_valid_port "CLICKHOUSE_POSTGRESQL_PORT"
! is_empty_value "$CLICKHOUSE_INTERSERVER_HTTP_PORT" && check_valid_port "CLICKHOUSE_INTERSERVER_HTTP_PORT"
# Validate credentials
if is_boolean_yes "${ALLOW_EMPTY_PASSWORD:-}"; then
warn "You set the environment variable ALLOW_EMPTY_PASSWORD=${ALLOW_EMPTY_PASSWORD:-}. For safety reasons, do not use this flag in a production environment."
elif is_empty_value "$CLICKHOUSE_ADMIN_PASSWORD"; then
print_validation_error "The CLICKHOUSE_ADMIN_PASSWORD environment variable is empty or not set. Set the environment variable ALLOW_EMPTY_PASSWORD=yes to allow a blank password. This is only recommended for development environments."
fi
return "$error_code"
}
########################
# Copy configuration from the mounted folder to the etc folder
# In charts mounting directly in the configuration folder would not
# allow the use of multiple ConfigMaps and Secrets
# Globals:
# CLICKHOUSE_*
# Arguments:
# None
# Returns:
# None
#########################
clickhouse_copy_mounted_configuration() {
if [[ -w "$CLICKHOUSE_CONF_DIR" ]]; then
if ! is_mounted_dir_empty "$CLICKHOUSE_MOUNTED_CONF_DIR"; then
info "Copying mounted configuration from $CLICKHOUSE_MOUNTED_CONF_DIR"
# Copy first the files at the base of the mounted folder to go to ClickHouse
# base etc folder
find "$CLICKHOUSE_MOUNTED_CONF_DIR" -maxdepth 1 -type f -exec cp -L {} "$CLICKHOUSE_CONF_DIR" \;
# The ClickHouse override directories (etc/conf.d and etc/users.d) do not support subfolders. That means we cannot
# copy directly with cp -RL because we need all override xml files to have at the root of these subfolders. In the helm
# chart we want to allow overrides from different ConfigMaps and Secrets so we need to use the find command
find "${CLICKHOUSE_MOUNTED_CONF_DIR}/conf.d" -type f -exec cp -L {} "${CLICKHOUSE_CONF_DIR}/conf.d" \;
find "${CLICKHOUSE_MOUNTED_CONF_DIR}/users.d" -type f -exec cp -L {} "${CLICKHOUSE_CONF_DIR}/users.d" \;
fi
else
warn "The folder $CLICKHOUSE_CONF_DIR is not writable. This is likely because a read-only filesystem was mounted in that folder. Using $CLICKHOUSE_MOUNTED_DIR is recommended"
fi
}
########################
# Add or modify an entry in the ClickHouse configuration file
# Globals:
# CLICKHOUSE_*
# Arguments:
# $1 - XPath expression
# $2 - Value to assign to the variable
# $3 - Configuration file
# Returns:
# None
#########################
clickhouse_conf_set() {
local -r xpath="${1:?key missing}"
# We allow empty values
local -r value="${2:-}"
local -r config_file="${3:-$CLICKHOUSE_CONF_FILE}"
debug "Setting ${xpath} to '${value}' in ClickHouse configuration file $config_file"
# Check if the entry exists in the XML file
if xmlstarlet --quiet sel -t -v "$xpath" "$config_file"; then
# Base case
# It exists, so replace the entry
if ! is_empty_value "$value"; then
xmlstarlet ed -L -u "$xpath" -v "$value" "$config_file"
fi
else
# It does not exist, so add the subnode
local -r parentNode="$(dirname "$xpath")"
local -r newNode="$(basename "$xpath")"
# Recursive call to add parent nodes
clickhouse_conf_set "$parentNode"
if is_empty_value "$value"; then
xmlstarlet ed -L --subnode "${parentNode}" -t "elem" -n "${newNode}" "$config_file"
else
xmlstarlet ed -L --subnode "${parentNode}" -t "elem" -n "${newNode}" -v "$value" "$config_file"
fi
fi
}
########################
# Check if ClickHouse daemon is running
# Arguments:
# None
# Returns:
# Boolean
#########################
is_clickhouse_running() {
pid="$(get_pid_from_file "$CLICKHOUSE_PID_FILE")"
if [[ -n "$pid" ]]; then
is_service_running "$pid"
else
false
fi
}
########################
# Check if ClickHouse daemon is not running
# Arguments:
# None
# Returns:
# Boolean
#########################
is_clickhouse_not_running() {
! is_clickhouse_running
}
########################
# Stop ClickHouse daemons
# Arguments:
# None
# Returns:
# None
#########################
clickhouse_stop() {
! is_clickhouse_running && return
stop_service_using_pid "$CLICKHOUSE_PID_FILE"
}
########################
# Initialize ClickHouse
# Arguments:
# None
# Returns:
# None
#########################
clickhouse_initialize() {
# Logic based on the upstream ClickHouse container
# For the container itself we keep the logic simple. In the helm chart we rely on the mounting of configuration files with overrides
# Source: https://github.com/ClickHouse/ClickHouse/blob/master/docker/server/entrypoint.sh
# This fixes an issue where the trap would kill the entrypoint.sh, if a PID was left over from a previous run
# Exec replaces the process without creating a new one, and when the container is restarted it may have the same PID
rm -f "$CLICKHOUSE_PID_FILE"
clickhouse_copy_mounted_configuration
if [[ "$CLICKHOUSE_ADMIN_USER" != "default" ]]; then
# If we need to set an admin user different from default, we create a configuration override
local -r admin_user_override="${CLICKHOUSE_CONF_DIR}/users.d/__bitnami_default_user.xml"
cat <<EOF >"${admin_user_override}"
<clickhouse>
<!-- Docs: <https://clickhouse.com/docs/en/operations/settings/settings_users/> -->
<users>
<!-- Remove default user -->
<default remove="remove">
</default>
<${CLICKHOUSE_ADMIN_USER}>
<profile>default</profile>
<password from_env="CLICKHOUSE_ADMIN_PASSWORD"></password>
<networks>
<ip>::/0</ip>
</networks>
<quota>default</quota>
<access_management>1</access_management>
</${CLICKHOUSE_ADMIN_USER}>
</users>
</clickhouse>
EOF
fi
}
########################
# Start ClickHouse daemon
# Arguments:
# $1 - Log file to check the startup message
# Returns:
# None
#########################
clickhouse_start_bg() {
local -r log_file="${1:-$CLICKHOUSE_LOG_FILE}"
info "Starting ClickHouse in background"
is_clickhouse_running && return
# This function is meant to be called for internal operations like the init scripts
local -r cmd=("${CLICKHOUSE_BASE_DIR}/bin/clickhouse-server")
local -r args=("--config-file=${CLICKHOUSE_CONF_FILE}" "--pid-file=${CLICKHOUSE_PID_FILE}" "--" "--listen_host=127.0.0.1")
if am_i_root; then
gosu "$CLICKHOUSE_DAEMON_USER" "${cmd[@]}" "${args[@]}" >"$log_file" 2>&1 &
else
"${cmd[@]}" "${args[@]}" >"$log_file" 2>&1 &
fi
if ! retry_while is_clickhouse_running; then
error "ClickHouse failed to start"
exit 1
fi
wait_for_log_entry "Application: Ready for connections" "$log_file"
info "ClickHouse started successfully"
}
########################
# Run custom scripts
# Globals:
# CLICKHOUSE_*
# Arguments:
# $1 - 'init' or 'start' ('init' runs on first container start, 'start' runs everytime the container starts)
# Returns:
# None
#########################
clickhouse_custom_scripts() {
if [[ -n $(find /docker-entrypoint-"$1"db.d/ -type f -regex ".*\.sh") ]] && { [[ ! -f "$CLICKHOUSE_DATA_DIR/.user_scripts_initialized" ]] || [[ $1 == start ]]; }; then
clickhouse_start_bg "$CLICKHOUSE_LOG_DIR/clickhouse_init_scripts.log"
info "Loading user's custom files from /docker-entrypoint-$1db.d"
for f in /docker-entrypoint-"$1"db.d/*; do
debug "Executing $f"
case "$f" in
*.sh)
if [[ -x "$f" ]]; then
if ! "$f"; then
error "Failed executing $f"
return 1
fi
else
warn "Sourcing $f as it is not executable by the current user, any error may cause initialization to fail"
. "$f"
fi
;;
*)
warn "Skipping $f, supported formats are: .sh"
;;
esac
done
touch "${CLICKHOUSE_DATA_DIR}/.user_scripts_initialized"
fi
}