bitnami-containers/bitnami/influxdb/1/ol-7/rootfs/libinfluxdb.sh

487 lines
15 KiB
Bash

#!/bin/bash
#
# Bitnami InfluxDB library
# shellcheck disable=SC1090
# shellcheck disable=SC1091
# Load Generic Libraries
. /liblog.sh
. /libos.sh
. /libvalidations.sh
# Functions
########################
# Load global variables used on InfluxDB configuration
# Globals:
# INFLUXDB_*
# Arguments:
# None
# Returns:
# Series of exports to be used as 'eval' arguments
#########################
influxdb_env() {
cat <<"EOF"
# Format log messages
export MODULE="influxdb"
export BITNAMI_DEBUG="${BITNAMI_DEBUG:-false}"
# Paths
export INFLUXDB_BASE_DIR="/opt/bitnami/influxdb"
export INFLUXDB_VOLUME_DIR="/bitnami/influxdb"
export INFLUXDB_BIN_DIR="${INFLUXDB_BASE_DIR}/bin"
export INFLUXDB_DATA_DIR="${INFLUXDB_DATA_DIR:-${INFLUXDB_VOLUME_DIR}/data}"
export INFLUXDB_DATA_WAL_DIR="${INFLUXDB_DATA_WAL_DIR:-${INFLUXDB_VOLUME_DIR}/wal}"
export INFLUXDB_META_DIR="${INFLUXDB_META_DIR:-${INFLUXDB_VOLUME_DIR}/meta}"
export INFLUXDB_CONF_DIR="${INFLUXDB_BASE_DIR}/etc"
export INFLUXDB_CONF_FILE="${INFLUXDB_CONF_DIR}/influxdb.conf"
export INFLUXDB_INITSCRIPTS_DIR="/docker-entrypoint-initdb.d"
# Users
export INFLUXDB_DAEMON_USER="influxdb"
export INFLUXDB_DAEMON_GROUP="influxdb"
# InfluxDB settings
export INFLUXDB_REPORTING_DISABLED="${INFLUXDB_REPORTING_DISABLED:-true}"
export INFLUXDB_HTTP_PORT_NUMBER="${INFLUXDB_HTTP_PORT_NUMBER:-8086}"
export INFLUXDB_HTTP_BIND_ADDRESS="${INFLUXDB_HTTP_BIND_ADDRESS:-0.0.0.0:${INFLUXDB_HTTP_PORT_NUMBER}}"
export INFLUXDB_PORT_NUMBER="${INFLUXDB_PORT_NUMBER:-8088}"
export INFLUXDB_BIND_ADDRESS="${INFLUXDB_BIND_ADDRESS:-0.0.0.0:${INFLUXDB_PORT_NUMBER}}"
# Authentication
export INFLUXDB_ADMIN_USER="${INFLUXDB_ADMIN_USER:-admin}"
export INFLUXDB_USER="${INFLUXDB_USER:-}"
export INFLUXDB_READ_USER="${INFLUXDB_READ_USER:-}"
export INFLUXDB_WRITE_USER="${INFLUXDB_WRITE_USER:-}"
export INFLUXDB_DB="${INFLUXDB_DB:-}"
EOF
# The configuration can be provided in a configuration file or environment variables
# This setting is necessary to determine certain validations/actions during the
# initialization, so we need to check the config file when existing.
if [[ -f "/opt/bitnami/influxdb/etc/influxdb.conf" ]]; then
cat <<"EOF"
INFLUXDB_HTTP_AUTH_ENABLED="${INFLUXDB_HTTP_AUTH_ENABLED:-$(influxdb_conf_get "auth-enabled")}"
export INFLUXDB_HTTP_AUTH_ENABLED="${INFLUXDB_HTTP_AUTH_ENABLED:-true}"
EOF
else
cat <<"EOF"
export INFLUXDB_HTTP_AUTH_ENABLED="${INFLUXDB_HTTP_AUTH_ENABLED:-true}"
EOF
fi
# Credentials should be allowed to be mounted as files to avoid sensitive data
# in the environment variables
if [[ -f "${INFLUXDB_ADMIN_USER_PASSWORD_FILE:-}" ]]; then
cat <<"EOF"
export INFLUXDB_ADMIN_USER_PASSWORD="$(< "${INFLUXDB_ADMIN_USER_PASSWORD_FILE}")"
EOF
else
cat <<"EOF"
export INFLUXDB_ADMIN_USER_PASSWORD="${INFLUXDB_ADMIN_USER_PASSWORD:-}"
EOF
fi
if [[ -f "${INFLUXDB_USER_PASSWORD_FILE:-}" ]]; then
cat <<"EOF"
export INFLUXDB_USER_PASSWORD="$(< "${INFLUXDB_USER_PASSWORD_FILE}")"
EOF
else
cat <<"EOF"
export INFLUXDB_USER_PASSWORD="${INFLUXDB_USER_PASSWORD:-}"
EOF
fi
if [[ -f "${INFLUXDB_READ_USER_PASSWORD_FILE:-}" ]]; then
cat <<"EOF"
export INFLUXDB_READ_USER_PASSWORD="$(< "${INFLUXDB_READ_USER_PASSWORD_FILE}")"
EOF
else
cat <<"EOF"
export INFLUXDB_READ_USER_PASSWORD="${INFLUXDB_READ_USER_PASSWORD:-}"
EOF
fi
if [[ -f "${INFLUXDB_WRITE_USER_PASSWORD_FILE:-}" ]]; then
cat <<"EOF"
export INFLUXDB_WRITE_USER_PASSWORD="$(< "${INFLUXDB_WRITE_USER_PASSWORD_FILE}")"
EOF
else
cat <<"EOF"
export INFLUXDB_WRITE_USER_PASSWORD="${INFLUXDB_WRITE_USER_PASSWORD:-}"
EOF
fi
}
########################
# Validate settings in INFLUXDB_* env vars
# Globals:
# INFLUXDB_*
# Arguments:
# None
# Returns:
# None
#########################
influxdb_validate() {
local error_code=0
debug "Validating settings in INFLUXDB_* env vars..."
# Auxiliary functions
print_validation_error() {
error "$1"
error_code=1
}
check_password_file() {
if ! is_empty_value "${!1:-}" && ! [[ -f "${!1:-}" ]]; then
print_validation_error "The variable $1 is defined but the file ${!1} is not accessible or does not exist."
fi
}
check_true_false_value() {
if ! is_true_false_value "${!1}"; then
print_validation_error "The allowed values for $1 are [true, false]"
fi
}
check_conflicting_ports() {
local -r total="$#"
for i in $(seq 1 "$((total - 1))"); do
for j in $(seq "$((i + 1))" "$total"); do
if [[ "${!i}" -eq "${!j}" ]]; then
print_validation_error "${!i} and ${!j} are bound to the same port"
fi
done
done
}
# InfluxDB secret files validations
local -a user_envs=("INFLUXDB_ADMIN_USER" "INFLUXDB_USER" "INFLUXDB_READ_USER" "INFLUXDB_WRITE_USER")
local -a pwd_file_envs=( "${user_envs[@]/%/_PASSWORD_FILE}" )
for pwd_file in "${pwd_file_envs[@]}"; do
check_password_file "$pwd_file"
done
# InfluxDB booleans validations
read -r -a boolean_envs <<< "$(compgen -A variable | grep -E "INFLUXDB_.*_(ENABLED|DISABLED)" | tr '\r\n' ' ')"
for boolean_env in "${boolean_envs[@]}"; do
check_true_false_value "$boolean_env"
done
# InfluxDB authentication validations
if ! is_boolean_yes "$INFLUXDB_HTTP_AUTH_ENABLED"; then
warn "Authentication is disabled over HTTP and HTTPS. For safety reasons, enable it in a production environment."
for user in "${user_envs[@]}"; do
if [[ -n "${!user:-}" ]]; then
warn "The ${user} environment variable will be ignored since authentication is disabled."
fi
done
else
for user in "${user_envs[@]}"; do
pwd="${user/%/_PASSWORD}"
if [[ -n "${!user:-}" ]] && [[ -z "${!pwd:-}" ]]; then
print_validation_error "Authentication is enabled over HTTP and HTTPS and you did not provide a password for the ${!user} user. Please, specify a password for the ${!user} user by setting the '${user/%/_PASSWORD}' or '${user/%/_PASSWORD_FILE}' environment variables."
fi
done
fi
# InfluxDB port validations
local -a ports_envs=("INFLUXDB_PORT_NUMBER" "INFLUXDB_HTTP_PORT_NUMBER")
for p in "${ports_envs[@]}"; do
if ! is_empty_value "${!p}" && ! err=$(validate_port -unprivileged "${!p}"); then
print_validation_error "An invalid port was specified in the environment variable ${p}: ${err}"
fi
done
check_conflicting_ports "${ports_envs[@]}"
[[ "$error_code" -eq 0 ]] || exit "$error_code"
}
########################
# Get a property's value from the the influxdb.conf file
# Globals:
# INFLUXDB_*
# Arguments:
# $1 - key
# $2 - section
# Returns:
# None
#########################
# TODO: use a golan binary (toml-parser)
influxdb_conf_get() {
local -r key="${1:?missing key}"
# local -r section="${2:?missing section}"
sed -n -e "s/^ *$key *= *//p" "$INFLUXDB_CONF_FILE"
# toml-parser -r "$section" "$key" "$INFLUXDB_CONF_FILE"
}
########################
# Modify the influxdb.conf file by setting a property
# Globals:
# INFLUXDB_*
# Arguments:
# $1 - section
# $2 - key
# $3 - value
# Returns:
# None
#########################
# TODO: use a golan binary (toml-parser) to perform these substitutions
# influxdb_conf_set() {
# local -r section="${1:?missing section}"
# local -r key="${2:?missing key}"
# local -r value="${2:-}"
#
# toml-parser -w "$section" "$key" "$value" "$INFLUXDB_CONF_FILE"
# }
########################
# Create basic influxdb.conf file using the example provided in the etc/ folder
# Globals:
# INFLUXDB_*
# Arguments:
# None
# Returns:
# None
#########################
influxdb_create_config() {
cp "${INFLUXDB_CONF_DIR}/influxdb.conf.default" "$INFLUXDB_CONF_FILE"
# TODO: use a golan binary (toml-parser) to perform these substitutions
# These settings:
# - [meta] dir
# - [data] dir
# - [data] wal-dir
# will be ignored and the values at the environment variables below will be used instead:
# - INFLUXDB_META_DIR
# - INFLUXDB_DATA_DIR
# - INFLUXDB_DATA_WAL_DIR
# However, to avoid confussion for users checking the configuration file,
# we'll update them to reflec the same values.
# influxdb_set_property "meta" "dir" "$INFLUXDB_META_DIR"
# influxdb_set_property "data" "dir" "$INFLUXDB_DATA_DIR"
# influxdb_set_property "data" "wal-dir" "$INFLUXDB_DATA_WAL_DIR"
}
########################
# Start InfluxDB in background disabling authentication and waits until it's ready
# Globals:
# INFLUXDB_*
# Arguments:
# None
# Returns:
# None
#########################
influxdb_start_bg_noauth() {
info "Starting InfluxDB in background..."
local start_command=("${INFLUXDB_BIN_DIR}/influxd" "-config" "$INFLUXDB_CONF_FILE")
am_i_root && start_command=("gosu" "$INFLUXDB_DAEMON_USER" "${start_command[@]}")
INFLUXDB_HTTP_HTTPS_ENABLED=false debug_execute "${start_command[@]}" &
wait-for-port "$INFLUXDB_PORT_NUMBER"
}
########################
# Check if InfluxDB is running
# Globals:
# INFLUXDB_*
# Arguments:
# None
# Returns:
# Boolean
#########################
is_influxdb_running() {
if pgrep "influxd" >/dev/null 2>&1; then
true
else
false
fi
}
########################
# Stop InfluxDB
# Globals:
# INFLUXDB_*
# Arguments:
# None
# Returns:
# None
#########################
influxdb_stop() {
info "Stopping InfluxDB..."
! is_influxdb_running && return
pkill --full --signal TERM "influxd"
wait-for-port --state free "$INFLUXDB_PORT_NUMBER"
}
########################
# Execute an arbitrary query using InfluxDB CLI
# Globals:
# INFLUXDB_*
# Arguments:
# $1 - Query to execute
# $2 - Whether to use admin credentials to run the command or not
# Returns:
# None
#########################
influxdb_execute_query() {
local -r query="${1:-query is required}"
local authenticate="${2:-false}"
local flags=("-host" "127.0.0.1" "-port" "$INFLUXDB_HTTP_PORT_NUMBER")
is_boolean_yes "$authenticate" && flags+=("-username" "${INFLUXDB_ADMIN_USER}" "-password" "${INFLUXDB_ADMIN_USER_PASSWORD}")
debug_execute "${INFLUXDB_BIN_DIR}/influx" "${flags[@]}" "-execute" "$query"
}
########################
# Creates the admin user
# Globals:
# INFLUXDB_*
# Arguments:
# None
# Returns:
# None
#########################
influxdb_create_admin_user() {
debug "Creating admin user..."
influxdb_execute_query "CREATE USER \"${INFLUXDB_ADMIN_USER}\" WITH PASSWORD '${INFLUXDB_ADMIN_USER_PASSWORD}' WITH ALL PRIVILEGES"
}
########################
# Creates a database
# Globals:
# INFLUXDB_*
# Arguments:
# $1 - Database name
# Returns:
# None
#########################
influxdb_create_db() {
local -r db="${1:?db is required}"
debug "Creating database \"${db}\"..."
influxdb_execute_query "CREATE DATABASE ${db}" "true"
}
########################
# Creates an user
# Globals:
# INFLUXDB_*
# Arguments:
# $1 - User name
# $2 - User password
# Returns:
# None
#########################
influxdb_create_user() {
local -r user="${1:?user is required}"
local -r pwd="${2:?pwd is required}"
debug "Creating user \"${user}\"..."
influxdb_execute_query "CREATE USER \"${user}\" WITH PASSWORD '${pwd}'" "true"
influxdb_execute_query "REVOKE ALL PRIVILEGES FROM \"${user}\"" "true"
}
########################
# Creates a database
# Globals:
# INFLUXDB_*
# Arguments:
# $1 - User name
# $2 - Database name
# $3 - Role
# Returns:
# None
#########################
influxdb_grant() {
local -r user="${1:?user is required}"
local -r db="${2:?db is required}"
local -r role="${3:?role is required}"
debug "Granting \"${role}\" permissions to user ${user} on database \"${db}\"..."
influxdb_execute_query "GRANT ${role} ON \"${db}\" TO \"${user}\"" "true"
}
########################
# Gets the role for an user
# Arguments:
# $1 - user
# Returns:
# String
#########################
influxdb_user_role() {
local role
local -r user="${1:?user is required}"
role="${user//_}"
role="${role%USER}"
role="${role#INFLUXDB}"
echo "${role:-ALL}"
}
########################
# Ensure InfluxDB is initialized
# Globals:
# INFLUXDB_*
# Arguments:
# None
# Returns:
# None
#########################
influxdb_initialize() {
info "Initializing InfluxDB..."
# Detect custom configuration files
if [[ -f "$INFLUXDB_CONF_FILE" ]]; then
info "Custom configuration ${INFLUXDB_CONF_FILE} detected!"
warn "The 'INFLUXDB_' environment variables override the equivalent options in the configuration file."
warn "If a configuration option is not specified in either the configuration file or in an environment variable, InfluxDB uses its internal default configuration"
else
info "No injected configuration files found. Creating default config files..."
influxdb_create_config
fi
local -r original_http_bind_address="$INFLUXDB_HTTP_BIND_ADDRESS"
export INFLUXDB_HTTP_BIND_ADDRESS="127.0.0.1:${INFLUXDB_HTTP_PORT_NUMBER}"
if is_dir_empty "$INFLUXDB_DATA_DIR"; then
info "Deploying InfluxDB from scratch"
if is_boolean_yes "$INFLUXDB_HTTP_AUTH_ENABLED"; then
influxdb_start_bg_noauth
info "Creating users and databases..."
influxdb_create_admin_user
[[ -n "$INFLUXDB_DB" ]] && influxdb_create_db "$INFLUXDB_DB"
local -a user_envs=("INFLUXDB_USER" "INFLUXDB_READ_USER" "INFLUXDB_WRITE_USER")
for user in "${user_envs[@]}"; do
pwd="${user/%/_PASSWORD}"
if [[ -n "${!user}" ]]; then
influxdb_create_user "${!user}" "${!pwd}"
[[ -n "$INFLUXDB_DB" ]] && influxdb_grant "${!user}" "$INFLUXDB_DB" "$(influxdb_user_role "$user")"
fi
done
fi
else
info "Deploying InfluxDB with persisted data"
fi
export INFLUXDB_HTTP_BIND_ADDRESS="$original_http_bind_address"
}
########################
# Run custom initialization scripts
# Globals:
# INFLUXDB_*
# Arguments:
# None
# Returns:
# None
#########################
influxdb_custom_init_scripts() {
if [[ -n $(find "${INFLUXDB_INITSCRIPTS_DIR}/" -type f -regex ".*\.\(sh\|txt\)") ]] && [[ ! -f "${INFLUXDB_INITSCRIPTS_DIR}/.user_scripts_initialized" ]] ; then
info "Loading user's custom files from ${INFLUXDB_INITSCRIPTS_DIR} ..."
local -r tmp_file="/tmp/filelist"
if ! is_influxdb_running; then
influxdb_start_bg_noauth
fi
find "${INFLUXDB_INITSCRIPTS_DIR}/" -type f -regex ".*\.\(sh\|txt\)" | sort > "$tmp_file"
while read -r f; do
case "$f" in
*.sh)
if [[ -x "$f" ]]; then
debug "Executing $f"; "$f"
else
debug "Sourcing $f"; . "$f"
fi
;;
*.txt) debug "Executing $f"; influxdb_execute_query "$(<"$f")";;
*) debug "Ignoring $f" ;;
esac
done < $tmp_file
rm -f "$tmp_file"
touch "$INFLUXDB_VOLUME_DIR"/.user_scripts_initialized
fi
}