mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2025-11-29 17:38:24 +08:00
feat(Config): Implement configuration severity policy and logging mechanism (#23284)
This commit is contained in:
19
.github/workflows/dashboard-ci.yml
vendored
19
.github/workflows/dashboard-ci.yml
vendored
@@ -74,11 +74,16 @@ jobs:
|
|||||||
|
|
||||||
- name: Configure AzerothCore settings
|
- name: Configure AzerothCore settings
|
||||||
run: |
|
run: |
|
||||||
# Create basic configuration
|
touch conf/config.sh
|
||||||
cp conf/dist/config.sh conf/config.sh
|
echo 'MTHREADS=4' >> conf/config.sh
|
||||||
# Configure dashboard
|
echo 'CBUILD_TESTING=ON' >> conf/config.sh
|
||||||
sed -i 's/MTHREADS=.*/MTHREADS="4"/' conf/config.sh
|
echo 'AC_ENABLE_ROOT_CMAKE_INSTALL=1' >> conf/config.sh
|
||||||
sed -i 's/CBUILD_TESTING=.*/CBUILD_TESTING="ON"/' conf/config.sh
|
echo 'export AC_CONFIG_POLICY=$AC_CONFIG_POLICY_PRESET_ZERO_CONF' >> conf/config.sh
|
||||||
|
echo 'AC_ENABLE_CONF_COPY_ON_INSTALL=0' >> conf/config.sh
|
||||||
|
cat conf/config.sh
|
||||||
|
|
||||||
|
# debug content of AC_CONFIG_POLICY
|
||||||
|
./acore.sh config show AC_CONFIG_POLICY
|
||||||
|
|
||||||
- name: Test module commands
|
- name: Test module commands
|
||||||
run: |
|
run: |
|
||||||
@@ -92,8 +97,6 @@ jobs:
|
|||||||
./acore.sh module update --all
|
./acore.sh module update --all
|
||||||
|
|
||||||
- name: Run complete installation (deps, compile, database, client-data)
|
- name: Run complete installation (deps, compile, database, client-data)
|
||||||
env:
|
|
||||||
AC_ENABLE_ROOT_CMAKE_INSTALL: 1
|
|
||||||
run: |
|
run: |
|
||||||
# This runs: install-deps, compile, database setup, client-data download
|
# This runs: install-deps, compile, database setup, client-data download
|
||||||
./acore.sh init
|
./acore.sh init
|
||||||
@@ -113,12 +116,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Test authserver dry-run
|
- name: Test authserver dry-run
|
||||||
run: |
|
run: |
|
||||||
|
source ./acore.sh config load
|
||||||
cd env/dist/bin
|
cd env/dist/bin
|
||||||
timeout 5m ./authserver -dry-run
|
timeout 5m ./authserver -dry-run
|
||||||
continue-on-error: false
|
continue-on-error: false
|
||||||
|
|
||||||
- name: Test worldserver dry-run
|
- name: Test worldserver dry-run
|
||||||
run: |
|
run: |
|
||||||
|
source ./acore.sh config load
|
||||||
cd env/dist/bin
|
cd env/dist/bin
|
||||||
timeout 5m ./worldserver -dry-run
|
timeout 5m ./worldserver -dry-run
|
||||||
continue-on-error: false
|
continue-on-error: false
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
function registerHooks() { acore_event_registerHooks "$@"; }
|
function registerHooks() { acore_event_registerHooks "$@"; }
|
||||||
function runHooks() { acore_event_runHooks "$@"; }
|
function runHooks() { acore_event_runHooks "$@"; }
|
||||||
|
|
||||||
#shellcheck source=../../conf/dist/config.sh
|
function acore_common_loadConfig() {
|
||||||
source "$AC_PATH_CONF/dist/config.sh" # include dist to avoid missing conf variables
|
#shellcheck source=../../conf/dist/config.sh
|
||||||
|
source "$AC_PATH_CONF/dist/config.sh" # include dist to avoid missing conf variables
|
||||||
|
|
||||||
# first check if it's defined in env, otherwise use the default
|
# first check if it's defined in env, otherwise use the default
|
||||||
USER_CONF_PATH=${USER_CONF_PATH:-"$AC_PATH_CONF/config.sh"}
|
USER_CONF_PATH=${USER_CONF_PATH:-"$AC_PATH_CONF/config.sh"}
|
||||||
|
|
||||||
if [ -f "$USER_CONF_PATH" ]; then
|
if [ -f "$USER_CONF_PATH" ]; then
|
||||||
source "$USER_CONF_PATH" # should overwrite previous
|
source "$USER_CONF_PATH" # should overwrite previous
|
||||||
else
|
else
|
||||||
echo "NOTICE: file <$USER_CONF_PATH> not found, we use default configuration only."
|
echo "NOTICE: file <$USER_CONF_PATH> not found, we use default configuration only."
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Load modules
|
# Load modules
|
||||||
|
|||||||
@@ -25,4 +25,6 @@ export AC_PATH_MODULES="$AC_PATH_ROOT/modules"
|
|||||||
|
|
||||||
export AC_PATH_DEPS="$AC_PATH_ROOT/deps"
|
export AC_PATH_DEPS="$AC_PATH_ROOT/deps"
|
||||||
|
|
||||||
|
export AC_BASH_LIB_PATH="$AC_PATH_DEPS/acore/bash-lib/src"
|
||||||
|
|
||||||
export AC_PATH_VAR="$AC_PATH_ROOT/var"
|
export AC_PATH_VAR="$AC_PATH_ROOT/var"
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ source "$AC_PATH_DEPS/acore/bash-lib/src/event/hooks.sh"
|
|||||||
# shellcheck source=./common.sh
|
# shellcheck source=./common.sh
|
||||||
source "$AC_PATH_SHARED/common.sh"
|
source "$AC_PATH_SHARED/common.sh"
|
||||||
|
|
||||||
|
acore_common_loadConfig
|
||||||
|
|
||||||
if [[ "$OSTYPE" = "msys" ]]; then
|
if [[ "$OSTYPE" = "msys" ]]; then
|
||||||
AC_BINPATH_FULL="$BINPATH"
|
AC_BINPATH_FULL="$BINPATH"
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# shellcheck source=../../../deps/acore/bash-lib/src/common/boolean.sh
|
||||||
|
source "$AC_BASH_LIB_PATH/common/boolean.sh"
|
||||||
|
|
||||||
# Set SUDO variable - one liner
|
# Set SUDO variable - one liner
|
||||||
SUDO=""
|
SUDO=""
|
||||||
|
|
||||||
@@ -135,7 +138,8 @@ function comp_compile() {
|
|||||||
echo "Done"
|
echo "Done"
|
||||||
;;
|
;;
|
||||||
linux*|darwin*)
|
linux*|darwin*)
|
||||||
local confDir=${CONFDIR:-"$AC_BINPATH_FULL/../etc"}
|
local confDir
|
||||||
|
confDir=${CONFDIR:-"$AC_BINPATH_FULL/../etc"}
|
||||||
|
|
||||||
# create the folders before installing to
|
# create the folders before installing to
|
||||||
# set the current user and permissions
|
# set the current user and permissions
|
||||||
@@ -145,6 +149,8 @@ function comp_compile() {
|
|||||||
mkdir -p "$confDir"
|
mkdir -p "$confDir"
|
||||||
mkdir -p "$confDir/modules"
|
mkdir -p "$confDir/modules"
|
||||||
|
|
||||||
|
confDir=$(realpath "$confDir")
|
||||||
|
|
||||||
echo "Cmake install..."
|
echo "Cmake install..."
|
||||||
$SUDO cmake --install . --config $CTYPE
|
$SUDO cmake --install . --config $CTYPE
|
||||||
|
|
||||||
@@ -161,18 +167,25 @@ function comp_compile() {
|
|||||||
$SUDO setcap cap_sys_nice=eip "$AC_BINPATH_FULL/authserver"
|
$SUDO setcap cap_sys_nice=eip "$AC_BINPATH_FULL/authserver"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[[ -f "$confDir/worldserver.conf.dist" ]] && \
|
|
||||||
cp -v --no-clobber "$confDir/worldserver.conf.dist" "$confDir/worldserver.conf"
|
|
||||||
[[ -f "$confDir/authserver.conf.dist" ]] && \
|
|
||||||
cp -v --no-clobber "$confDir/authserver.conf.dist" "$confDir/authserver.conf"
|
|
||||||
[[ -f "$confDir/dbimport.conf.dist" ]] && \
|
|
||||||
cp -v --no-clobber "$confDir/dbimport.conf.dist" "$confDir/dbimport.conf"
|
|
||||||
|
|
||||||
for f in "$confDir/modules/"*.dist
|
if ( isTrue "$AC_ENABLE_CONF_COPY_ON_INSTALL" ) then
|
||||||
do
|
echo "Copying default configuration files to $confDir ..."
|
||||||
[[ -e $f ]] || break # handle the case of no *.dist files
|
[[ -f "$confDir/worldserver.conf.dist" && ! -f "$confDir/worldserver.conf" ]] && \
|
||||||
cp -v --no-clobber "$f" "${f%.dist}";
|
cp -v "$confDir/worldserver.conf.dist" "$confDir/worldserver.conf"
|
||||||
done
|
[[ -f "$confDir/authserver.conf.dist" && ! -f "$confDir/authserver.conf" ]] && \
|
||||||
|
cp -v "$confDir/authserver.conf.dist" "$confDir/authserver.conf"
|
||||||
|
[[ -f "$confDir/dbimport.conf.dist" && ! -f "$confDir/dbimport.conf" ]] && \
|
||||||
|
cp -v "$confDir/dbimport.conf.dist" "$confDir/dbimport.conf"
|
||||||
|
|
||||||
|
for f in "$confDir/modules/"*.dist
|
||||||
|
do
|
||||||
|
[[ -e $f ]] || break # handle the case of no *.dist files
|
||||||
|
if [[ ! -f "${f%.dist}" ]]; then
|
||||||
|
echo "Copying module config $(basename "${f%.dist}")"
|
||||||
|
cp -v "$f" "${f%.dist}";
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Done"
|
echo "Done"
|
||||||
;;
|
;;
|
||||||
|
|||||||
9
apps/installer/includes/config/config-main.sh
Normal file
9
apps/installer/includes/config/config-main.sh
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd )
|
||||||
|
|
||||||
|
# shellcheck source=./config.sh
|
||||||
|
source "$CURRENT_PATH/config.sh"
|
||||||
|
|
||||||
|
acore_dash_config "$@"
|
||||||
|
|
||||||
60
apps/installer/includes/config/config.sh
Normal file
60
apps/installer/includes/config/config.sh
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd )
|
||||||
|
|
||||||
|
# shellcheck source=../../../bash_shared/includes.sh
|
||||||
|
source "$CURRENT_PATH/../../../bash_shared/includes.sh"
|
||||||
|
# shellcheck source=../includes.sh
|
||||||
|
source "$CURRENT_PATH/../includes.sh"
|
||||||
|
# shellcheck source=../../../bash_shared/menu_system.sh
|
||||||
|
source "$AC_PATH_APPS/bash_shared/menu_system.sh"
|
||||||
|
|
||||||
|
function acore_dash_configShowValue() {
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo "Usage: show <VAR_NAME>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local varName="$1"
|
||||||
|
local varValue="${!varName}"
|
||||||
|
if [ -z "$varValue" ]; then
|
||||||
|
echo "$varName is not set."
|
||||||
|
else
|
||||||
|
echo "$varName=$varValue"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function acore_dash_configLoad() {
|
||||||
|
acore_common_loadConfig
|
||||||
|
echo "Configuration loaded into the current shell session."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configuration management menu definition
|
||||||
|
# Format: "key|short|description"
|
||||||
|
config_menu_items=(
|
||||||
|
"show|s|Show configuration variable value"
|
||||||
|
"load|l|Load configurations variables within the current shell session"
|
||||||
|
"help|h|Show detailed help"
|
||||||
|
"quit|q|Close this menu"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Menu command handler for configuration operations
|
||||||
|
function handle_config_command() {
|
||||||
|
local key="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
case "$key" in
|
||||||
|
"show")
|
||||||
|
acore_dash_configShowValue "$@"
|
||||||
|
;;
|
||||||
|
"load")
|
||||||
|
acore_dash_configLoad
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
function acore_dash_config() {
|
||||||
|
menu_run_with_items "CONFIG MANAGER" handle_config_command -- "${config_menu_items[@]}" -- "$@"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
@@ -183,3 +183,5 @@ function inst_download_client_data {
|
|||||||
&& echo "Remove downloaded file" && rm "$zipPath" \
|
&& echo "Remove downloaded file" && rm "$zipPath" \
|
||||||
&& echo "INSTALLED_VERSION=$VERSION" > "$dataVersionFile"
|
&& echo "INSTALLED_VERSION=$VERSION" > "$dataVersionFile"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd )
|
CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd )
|
||||||
|
|
||||||
|
# shellcheck source=../../bash_shared/includes.sh
|
||||||
source "$CURRENT_PATH/../../bash_shared/includes.sh"
|
source "$CURRENT_PATH/../../bash_shared/includes.sh"
|
||||||
|
|
||||||
AC_PATH_INSTALLER="$AC_PATH_APPS/installer"
|
AC_PATH_INSTALLER="$AC_PATH_APPS/installer"
|
||||||
@@ -9,14 +10,14 @@ AC_PATH_INSTALLER="$AC_PATH_APPS/installer"
|
|||||||
J_PATH="$AC_PATH_DEPS/acore/joiner"
|
J_PATH="$AC_PATH_DEPS/acore/joiner"
|
||||||
J_PATH_MODULES="$AC_PATH_MODULES"
|
J_PATH_MODULES="$AC_PATH_MODULES"
|
||||||
|
|
||||||
|
# shellcheck source=../../../deps/acore/joiner/joiner.sh
|
||||||
source "$J_PATH/joiner.sh"
|
source "$J_PATH/joiner.sh"
|
||||||
|
|
||||||
if [ -f "$AC_PATH_INSTALLER/config.sh" ]; then
|
# shellcheck source=../../compiler/includes/includes.sh
|
||||||
source "$AC_PATH_INSTALLER/config.sh" # should overwrite previous
|
|
||||||
fi
|
|
||||||
|
|
||||||
source "$AC_PATH_APPS/compiler/includes/includes.sh"
|
source "$AC_PATH_APPS/compiler/includes/includes.sh"
|
||||||
|
|
||||||
|
# shellcheck source=../../../deps/semver_bash/semver.sh
|
||||||
source "$AC_PATH_DEPS/semver_bash/semver.sh"
|
source "$AC_PATH_DEPS/semver_bash/semver.sh"
|
||||||
|
|
||||||
|
# shellcheck source=../includes/functions.sh
|
||||||
source "$AC_PATH_INSTALLER/includes/functions.sh"
|
source "$AC_PATH_INSTALLER/includes/functions.sh"
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ else
|
|||||||
C_GREEN=''
|
C_GREEN=''
|
||||||
C_YELLOW=''
|
C_YELLOW=''
|
||||||
C_BLUE=''
|
C_BLUE=''
|
||||||
C_MAGENTA=''
|
|
||||||
C_CYAN=''
|
C_CYAN=''
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -174,42 +173,8 @@ function inst_module_list() {
|
|||||||
# Usage: ./acore.sh module <search|install|update|remove> [args...]
|
# Usage: ./acore.sh module <search|install|update|remove> [args...]
|
||||||
# ./acore.sh module # Interactive menu
|
# ./acore.sh module # Interactive menu
|
||||||
function inst_module() {
|
function inst_module() {
|
||||||
# If no arguments provided, start interactive menu
|
menu_run_with_items "MODULE MANAGER" handle_module_command -- "${module_menu_items[@]}" -- "$@"
|
||||||
if [[ $# -eq 0 ]]; then
|
return $?
|
||||||
menu_run_with_items "MODULE MANAGER" handle_module_command -- "${module_menu_items[@]}" --
|
|
||||||
return $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Normalize arguments into an array
|
|
||||||
local tokens=()
|
|
||||||
read -r -a tokens <<< "$*"
|
|
||||||
local cmd="${tokens[0]}"
|
|
||||||
local args=("${tokens[@]:1}")
|
|
||||||
|
|
||||||
case "$cmd" in
|
|
||||||
""|"help"|"-h"|"--help")
|
|
||||||
inst_module_help
|
|
||||||
;;
|
|
||||||
"search"|"s")
|
|
||||||
inst_module_search "${args[@]}"
|
|
||||||
;;
|
|
||||||
"install"|"i")
|
|
||||||
inst_module_install "${args[@]}"
|
|
||||||
;;
|
|
||||||
"update"|"u")
|
|
||||||
inst_module_update "${args[@]}"
|
|
||||||
;;
|
|
||||||
"remove"|"r")
|
|
||||||
inst_module_remove "${args[@]}"
|
|
||||||
;;
|
|
||||||
"list"|"l")
|
|
||||||
inst_module_list "${args[@]}"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
print_error "Unknown module command: $cmd. Use 'help' to see available commands."
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ menu_items=(
|
|||||||
"docker|dr|Run docker tools"
|
"docker|dr|Run docker tools"
|
||||||
"version|v|Show AzerothCore version"
|
"version|v|Show AzerothCore version"
|
||||||
"service-manager|sm|Run service manager to run authserver and worldserver in background"
|
"service-manager|sm|Run service manager to run authserver and worldserver in background"
|
||||||
|
"config|cf|Configuration manager"
|
||||||
"quit|q|Exit from this menu"
|
"quit|q|Exit from this menu"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -100,6 +101,9 @@ function handle_menu_command() {
|
|||||||
bash "$AC_PATH_APPS/startup-scripts/src/service-manager.sh" "$@"
|
bash "$AC_PATH_APPS/startup-scripts/src/service-manager.sh" "$@"
|
||||||
exit
|
exit
|
||||||
;;
|
;;
|
||||||
|
"config")
|
||||||
|
bash "$AC_PATH_APPS/installer/includes/config/config-main.sh" "$@"
|
||||||
|
;;
|
||||||
"quit")
|
"quit")
|
||||||
echo "Goodbye!"
|
echo "Goodbye!"
|
||||||
exit
|
exit
|
||||||
|
|||||||
@@ -751,5 +751,5 @@ EOF
|
|||||||
|
|
||||||
run inst_module "unknown-command"
|
run inst_module "unknown-command"
|
||||||
[ "$status" -eq 1 ]
|
[ "$status" -eq 1 ]
|
||||||
[[ "$output" =~ "Unknown module command" ]]
|
[[ "$output" =~ "Invalid option" ]]
|
||||||
}
|
}
|
||||||
@@ -51,6 +51,8 @@ fi
|
|||||||
while true; do
|
while true; do
|
||||||
STARTING_TIME=$(date +%s)
|
STARTING_TIME=$(date +%s)
|
||||||
|
|
||||||
|
echo "AC_CONFIG_POLICY: $AC_CONFIG_POLICY"
|
||||||
|
|
||||||
# Use starter script to launch the binary with all parameters
|
# Use starter script to launch the binary with all parameters
|
||||||
"$STARTER_SCRIPT" "$BINPATH" "$BINFILE" "$GDB_FILE" "$CONFIG" "$SYSLOG" "$SYSERR" "$GDB_ENABLED" "$CRASHES_PATH"
|
"$STARTER_SCRIPT" "$BINPATH" "$BINFILE" "$GDB_FILE" "$CONFIG" "$SYSLOG" "$SYSERR" "$GDB_ENABLED" "$CRASHES_PATH"
|
||||||
|
|
||||||
|
|||||||
36
conf/dist/config.sh
vendored
36
conf/dist/config.sh
vendored
@@ -118,6 +118,12 @@ export CCACHE_DIR=${CCACHE_DIR:-"$AC_PATH_VAR/ccache"}
|
|||||||
#
|
#
|
||||||
export AC_ENABLE_ROOT_CMAKE_INSTALL=${AC_ENABLE_ROOT_CMAKE_INSTALL:-0}
|
export AC_ENABLE_ROOT_CMAKE_INSTALL=${AC_ENABLE_ROOT_CMAKE_INSTALL:-0}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Enable copying configuration files on install
|
||||||
|
# Default: 1 (true)
|
||||||
|
#
|
||||||
|
export AC_ENABLE_CONF_COPY_ON_INSTALL=${AC_ENABLE_CONF_COPY_ON_INSTALL:-1}
|
||||||
|
|
||||||
##############################################
|
##############################################
|
||||||
#
|
#
|
||||||
# GOOGLE PERF TOOLS
|
# GOOGLE PERF TOOLS
|
||||||
@@ -182,4 +188,34 @@ export MODULES_EXCLUDE_LIST=""
|
|||||||
NO_COLOR=${NO_COLOR:-}
|
NO_COLOR=${NO_COLOR:-}
|
||||||
FORCE_COLOR=${FORCE_COLOR:-}
|
FORCE_COLOR=${FORCE_COLOR:-}
|
||||||
|
|
||||||
|
##############################################
|
||||||
|
#
|
||||||
|
# CONFIGURATION SEVERITY POLICY
|
||||||
|
#
|
||||||
|
# Controls how the core reacts to missing configuration files,
|
||||||
|
# missing/unknown options and invalid values.
|
||||||
|
# The policy string follows the format "key=severity" separated by commas.
|
||||||
|
# Supported severities: skip, warn, error, fatal.
|
||||||
|
# Possible keys: default, missing_file, missing_option, critical_option,
|
||||||
|
# unknown_option, value_error.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# export AC_CONFIG_POLICY="$AC_CONFIG_POLICY_PRESET_DEFAULT"
|
||||||
|
# export AC_CONFIG_POLICY="default=skip,critical_option=fatal,unknown_option=warn"
|
||||||
|
# export AC_CONFIG_POLICY="missing_file=fatal,missing_option=error"
|
||||||
|
#
|
||||||
|
# Presets:
|
||||||
|
# AC_CONFIG_POLICY_PRESET_DEFAULT -> mirrors the core default behaviour
|
||||||
|
# (errors on missing files, fatal on critical)
|
||||||
|
# AC_CONFIG_POLICY_PRESET_ZERO_CONF -> skips non-critical gaps so the core
|
||||||
|
# can boot from environment defaults
|
||||||
|
# AC_CONFIG_POLICY_PRESET_STRICT -> escalates everything to errors/fatals
|
||||||
|
#
|
||||||
|
|
||||||
|
export AC_CONFIG_POLICY_PRESET_ZERO_CONF='default=skip'
|
||||||
|
export AC_CONFIG_POLICY_PRESET_DEFAULT='missing_file=error,missing_option=warn,critical_option=fatal,unknown_option=error,value_error=error'
|
||||||
|
export AC_CONFIG_POLICY_PRESET_STRICT='default=error,missing_file=fatal,missing_option=error,critical_option=fatal,unknown_option=error,value_error=error'
|
||||||
|
|
||||||
|
export AC_CONFIG_POLICY=$AC_CONFIG_POLICY_PRESET_DEFAULT
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
5
deps/acore/bash-lib/src/common/boolean.sh
vendored
Normal file
5
deps/acore/bash-lib/src/common/boolean.sh
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
function isTrue() {
|
||||||
|
local val
|
||||||
|
val=$(echo "$1" | tr '[:upper:]' '[:lower:]')
|
||||||
|
[[ "$val" == "1" || "$val" == "true" || "$val" == "yes" || "$val" == "on" ]]
|
||||||
|
}
|
||||||
101
doc/ConfigPolicy.md
Normal file
101
doc/ConfigPolicy.md
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
# Configuration Severity Policy
|
||||||
|
|
||||||
|
The configuration loader can decide how strictly it should react when it
|
||||||
|
encounters missing files, undefined options or invalid values. This document
|
||||||
|
describes the available knobs and provides ready-to-use presets.
|
||||||
|
|
||||||
|
## Severity Levels
|
||||||
|
|
||||||
|
Each policy entry maps a **key** to one of the following severities:
|
||||||
|
|
||||||
|
| Severity | Description |
|
||||||
|
|----------|-----------------------------------------------------------------------------|
|
||||||
|
| `skip` | Ignore the problem and continue silently. |
|
||||||
|
| `warn` | Log a warning and continue. |
|
||||||
|
| `error` | Log an error and continue (useful to surface issues without aborting). |
|
||||||
|
| `fatal` | Log a fatal message and abort the process immediately. |
|
||||||
|
|
||||||
|
## Policy Keys
|
||||||
|
|
||||||
|
The following keys can be customised:
|
||||||
|
|
||||||
|
| Key | Applies to |
|
||||||
|
|--------------------|----------------------------------------------------------------------|
|
||||||
|
| `default` | Fallback severity for any key that is not explicitly overridden. |
|
||||||
|
| `missing_file` | Missing or empty configuration files (worldserver.conf, modules, …). |
|
||||||
|
| `missing_option` | Options looked up in code but not present in any config file. |
|
||||||
|
| `critical_option` | Required options (`RealmID`, `*DatabaseInfo`, …). |
|
||||||
|
| `unknown_option` | Options found in optional configs that the core does not recognise. |
|
||||||
|
| `value_error` | Options that cannot be converted to the expected type. |
|
||||||
|
|
||||||
|
> Critical options remain fatal by default to prevent the core from booting with
|
||||||
|
> incomplete database details; you can relax them if required.
|
||||||
|
|
||||||
|
## Configuration Channels
|
||||||
|
|
||||||
|
### `config.sh`
|
||||||
|
|
||||||
|
`conf/dist/config.sh` exposes the `AC_CONFIG_POLICY` variable alongside a few
|
||||||
|
presets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Mirrors the default behaviour (errors, with fatal criticals)
|
||||||
|
export AC_CONFIG_POLICY="$AC_CONFIG_POLICY_PRESET_DEFAULT"
|
||||||
|
|
||||||
|
# Skip anything non-critical so the core can bootstrap from defaults + env vars
|
||||||
|
export AC_CONFIG_POLICY="$AC_CONFIG_POLICY_PRESET_ZERO_CONF"
|
||||||
|
|
||||||
|
# Treat everything strictly (useful for CI)
|
||||||
|
export AC_CONFIG_POLICY="$AC_CONFIG_POLICY_PRESET_STRICT"
|
||||||
|
```
|
||||||
|
|
||||||
|
The presets are defined as:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
AC_CONFIG_POLICY_PRESET_DEFAULT='missing_file=error,missing_option=warn,critical_option=fatal,unknown_option=error,value_error=error'
|
||||||
|
AC_CONFIG_POLICY_PRESET_ZERO_CONF='default=skip,critical_option=fatal,unknown_option=warn,value_error=warn'
|
||||||
|
AC_CONFIG_POLICY_PRESET_STRICT='default=error,missing_file=fatal,missing_option=error,critical_option=fatal,unknown_option=error,value_error=error'
|
||||||
|
```
|
||||||
|
|
||||||
|
Modify or extend these entries to suit your deployment.
|
||||||
|
|
||||||
|
### Environment Variable
|
||||||
|
|
||||||
|
The runtime honours the `AC_CONFIG_POLICY` environment variable, so you can
|
||||||
|
override the policy without editing `config.sh`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export AC_CONFIG_POLICY="default=skip,critical_option=fatal"
|
||||||
|
./acore.sh run-worldserver
|
||||||
|
```
|
||||||
|
|
||||||
|
### CLI Override
|
||||||
|
|
||||||
|
Every server/tool executable accepts `--config-policy`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./bin/worldserver --config-policy="missing_file=fatal,unknown_option=warn"
|
||||||
|
./bin/authserver --config-policy "$AC_CONFIG_POLICY_PRESET_STRICT"
|
||||||
|
```
|
||||||
|
|
||||||
|
The CLI flag takes precedence over the environment and `config.sh`.
|
||||||
|
|
||||||
|
## Quick Presets
|
||||||
|
|
||||||
|
| Preset | Intended use |
|
||||||
|
|---------------|---------------------------------------------------------------------------|
|
||||||
|
| `legacy` | Default behaviour before this feature (errors for missing files/options). |
|
||||||
|
| `zero-conf` | Zero-touch deployments; rely on defaults/env vars where possible. |
|
||||||
|
| `strict` | Fail-fast in CI or controlled environments. |
|
||||||
|
|
||||||
|
Feel free to clone these presets and store your own variants inside
|
||||||
|
`config.sh` or deployment scripts.
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
|
||||||
|
- Pair `fatal` severities with monitoring so regressions in configuration
|
||||||
|
surface quickly.
|
||||||
|
- When experimenting locally, start with `zero-conf` and elevate specific keys
|
||||||
|
to `error`/`fatal` as you validate your setup.
|
||||||
|
- Remember that number parsing errors (`value_error`) often indicate typos;
|
||||||
|
keep them at least `error` unless you have a very good reason.
|
||||||
@@ -21,10 +21,14 @@
|
|||||||
#include "StringFormat.h"
|
#include "StringFormat.h"
|
||||||
#include "Tokenize.h"
|
#include "Tokenize.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <locale>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -34,13 +38,14 @@ namespace
|
|||||||
std::unordered_map<std::string /*name*/, std::string /*value*/> _configOptions;
|
std::unordered_map<std::string /*name*/, std::string /*value*/> _configOptions;
|
||||||
std::unordered_map<std::string /*name*/, std::string /*value*/> _envVarCache;
|
std::unordered_map<std::string /*name*/, std::string /*value*/> _envVarCache;
|
||||||
std::mutex _configLock;
|
std::mutex _configLock;
|
||||||
|
ConfigPolicy _policy;
|
||||||
|
|
||||||
std::vector<std::string> _fatalConfigOptions =
|
std::unordered_set<std::string> _criticalConfigOptions =
|
||||||
{
|
{
|
||||||
{ "RealmID" },
|
"RealmID",
|
||||||
{ "LoginDatabaseInfo" },
|
"LoginDatabaseInfo",
|
||||||
{ "WorldDatabaseInfo" },
|
"WorldDatabaseInfo",
|
||||||
{ "CharacterDatabaseInfo" },
|
"CharacterDatabaseInfo",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check system configs like *server.conf*
|
// Check system configs like *server.conf*
|
||||||
@@ -62,6 +67,29 @@ namespace
|
|||||||
return foundAppender != std::string_view::npos || foundLogger != std::string_view::npos;
|
return foundAppender != std::string_view::npos || foundLogger != std::string_view::npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<ConfigSeverity> ParseSeverity(std::string_view value)
|
||||||
|
{
|
||||||
|
if (value.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
std::string lowered(value);
|
||||||
|
std::transform(lowered.begin(), lowered.end(), lowered.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||||
|
|
||||||
|
if (lowered == "skip")
|
||||||
|
return ConfigSeverity::Skip;
|
||||||
|
|
||||||
|
if (lowered == "warn" || lowered == "warning")
|
||||||
|
return ConfigSeverity::Warn;
|
||||||
|
|
||||||
|
if (lowered == "error")
|
||||||
|
return ConfigSeverity::Error;
|
||||||
|
|
||||||
|
if (lowered == "fatal" || lowered == "abort" || lowered == "panic")
|
||||||
|
return ConfigSeverity::Fatal;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Format, typename... Args>
|
template<typename Format, typename... Args>
|
||||||
inline void PrintError(std::string_view filename, Format&& fmt, Args&& ... args)
|
inline void PrintError(std::string_view filename, Format&& fmt, Args&& ... args)
|
||||||
{
|
{
|
||||||
@@ -77,6 +105,138 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Format, typename... Args>
|
||||||
|
inline void LogWithSeverity(ConfigSeverity severity, std::string_view filename, Format&& fmt, Args&&... args)
|
||||||
|
{
|
||||||
|
std::string message = Acore::StringFormat(std::forward<Format>(fmt), std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
switch (severity)
|
||||||
|
{
|
||||||
|
case ConfigSeverity::Skip:
|
||||||
|
return;
|
||||||
|
case ConfigSeverity::Warn:
|
||||||
|
{
|
||||||
|
if (IsAppConfig(filename))
|
||||||
|
fmt::print("{}\n", message);
|
||||||
|
|
||||||
|
LOG_WARN("server.loading", message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case ConfigSeverity::Error:
|
||||||
|
{
|
||||||
|
if (IsAppConfig(filename))
|
||||||
|
fmt::print("{}\n", message);
|
||||||
|
|
||||||
|
LOG_ERROR("server.loading", message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case ConfigSeverity::Fatal:
|
||||||
|
{
|
||||||
|
if (IsAppConfig(filename))
|
||||||
|
fmt::print("{}\n", message);
|
||||||
|
|
||||||
|
LOG_FATAL("server.loading", message);
|
||||||
|
ABORT(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigPolicy ApplyPolicyString(ConfigPolicy policy, std::string_view input)
|
||||||
|
{
|
||||||
|
if (input.empty())
|
||||||
|
return policy;
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, ConfigSeverity>> overrides;
|
||||||
|
Optional<ConfigSeverity> defaultOverride;
|
||||||
|
|
||||||
|
std::string tokenBuffer(input);
|
||||||
|
for (std::string_view rawToken : Acore::Tokenize(tokenBuffer, ',', false))
|
||||||
|
{
|
||||||
|
std::string token = Acore::String::Trim(std::string(rawToken), std::locale());
|
||||||
|
if (token.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto separator = token.find('=');
|
||||||
|
if (separator == std::string::npos)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string key = Acore::String::Trim(token.substr(0, separator), std::locale());
|
||||||
|
std::string value = Acore::String::Trim(token.substr(separator + 1), std::locale());
|
||||||
|
|
||||||
|
if (key.empty() || value.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto severity = ParseSeverity(value);
|
||||||
|
if (!severity)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::transform(key.begin(), key.end(), key.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||||
|
|
||||||
|
if (key == "default")
|
||||||
|
{
|
||||||
|
defaultOverride = severity;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
overrides.emplace_back(std::move(key), *severity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultOverride)
|
||||||
|
{
|
||||||
|
policy.defaultSeverity = *defaultOverride;
|
||||||
|
policy.missingFileSeverity = *defaultOverride;
|
||||||
|
policy.missingOptionSeverity = *defaultOverride;
|
||||||
|
policy.criticalOptionSeverity = *defaultOverride;
|
||||||
|
policy.unknownOptionSeverity = *defaultOverride;
|
||||||
|
policy.valueErrorSeverity = *defaultOverride;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& [key, severity] : overrides)
|
||||||
|
{
|
||||||
|
if (key == "missing_file" || key == "file")
|
||||||
|
policy.missingFileSeverity = severity;
|
||||||
|
else if (key == "missing_option" || key == "option")
|
||||||
|
policy.missingOptionSeverity = severity;
|
||||||
|
else if (key == "critical_option" || key == "critical")
|
||||||
|
policy.criticalOptionSeverity = severity;
|
||||||
|
else if (key == "unknown_option" || key == "unknown")
|
||||||
|
policy.unknownOptionSeverity = severity;
|
||||||
|
else if (key == "value_error" || key == "value")
|
||||||
|
policy.valueErrorSeverity = severity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigPolicy ApplyPolicyFromArgs(ConfigPolicy policy, std::vector<std::string> const& args)
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < args.size(); ++i)
|
||||||
|
{
|
||||||
|
std::string const& arg = args[i];
|
||||||
|
std::string_view value;
|
||||||
|
|
||||||
|
constexpr std::string_view shortOpt = "--config-policy";
|
||||||
|
|
||||||
|
if (arg.rfind(shortOpt, 0) == 0)
|
||||||
|
{
|
||||||
|
if (arg.size() == shortOpt.size() && (i + 1) < args.size())
|
||||||
|
{
|
||||||
|
value = args[i + 1];
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
else if (arg.size() > shortOpt.size() && arg[shortOpt.size()] == '=')
|
||||||
|
{
|
||||||
|
value = std::string_view(arg).substr(shortOpt.size() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value.empty())
|
||||||
|
policy = ApplyPolicyString(policy, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return policy;
|
||||||
|
}
|
||||||
|
|
||||||
void AddKey(std::string const& optionName, std::string const& optionKey, std::string_view fileName, bool isOptional, [[maybe_unused]] bool isReload)
|
void AddKey(std::string const& optionName, std::string const& optionKey, std::string_view fileName, bool isOptional, [[maybe_unused]] bool isReload)
|
||||||
{
|
{
|
||||||
auto const& itr = _configOptions.find(optionName);
|
auto const& itr = _configOptions.find(optionName);
|
||||||
@@ -86,7 +246,7 @@ namespace
|
|||||||
{
|
{
|
||||||
if (!IsLoggingSystemOptions(optionName) && !isReload)
|
if (!IsLoggingSystemOptions(optionName) && !isReload)
|
||||||
{
|
{
|
||||||
PrintError(fileName, "> Config::LoadFile: Found incorrect option '{}' in config file '{}'. Skip", optionName, fileName);
|
LogWithSeverity(_policy.unknownOptionSeverity, fileName, "> Config::LoadFile: Found incorrect option '{}' in config file '{}'. Skip", optionName, fileName);
|
||||||
|
|
||||||
#ifdef CONFIG_ABORT_INCORRECT_OPTIONS
|
#ifdef CONFIG_ABORT_INCORRECT_OPTIONS
|
||||||
ABORT("> Core can't start if found incorrect options");
|
ABORT("> Core can't start if found incorrect options");
|
||||||
@@ -111,13 +271,10 @@ namespace
|
|||||||
|
|
||||||
if (in.fail())
|
if (in.fail())
|
||||||
{
|
{
|
||||||
if (isOptional)
|
ConfigSeverity severity = isOptional ? ConfigSeverity::Skip : _policy.missingFileSeverity;
|
||||||
{
|
LogWithSeverity(severity, file, "> Config::LoadFile: Failed open {}file '{}'", isOptional ? "optional " : "", file);
|
||||||
// No display erorr if file optional
|
// Treat SKIP as a successful no-op so the app can proceed
|
||||||
return false;
|
return severity == ConfigSeverity::Skip;
|
||||||
}
|
|
||||||
|
|
||||||
throw ConfigException(Acore::StringFormat("Config::LoadFile: Failed open {}file '{}'", isOptional ? "optional " : "", file));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 count = 0;
|
uint32 count = 0;
|
||||||
@@ -181,13 +338,10 @@ namespace
|
|||||||
// No lines read
|
// No lines read
|
||||||
if (!count)
|
if (!count)
|
||||||
{
|
{
|
||||||
if (isOptional)
|
ConfigSeverity severity = isOptional ? ConfigSeverity::Skip : _policy.missingFileSeverity;
|
||||||
{
|
LogWithSeverity(severity, file, "> Config::LoadFile: Empty file '{}'", file);
|
||||||
// No display erorr if file optional
|
// Treat SKIP as a successful no-op
|
||||||
return false;
|
return severity == ConfigSeverity::Skip;
|
||||||
}
|
|
||||||
|
|
||||||
throw ConfigException(Acore::StringFormat("Config::LoadFile: Empty file '{}'", file));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add correct keys if file load without errors
|
// Add correct keys if file load without errors
|
||||||
@@ -382,7 +536,6 @@ T ConfigMgr::GetValueDefault(std::string const& name, T const& def, bool showLog
|
|||||||
std::string strValue;
|
std::string strValue;
|
||||||
|
|
||||||
auto const& itr = _configOptions.find(name);
|
auto const& itr = _configOptions.find(name);
|
||||||
bool fatalConfig = false;
|
|
||||||
bool notFound = itr == _configOptions.end();
|
bool notFound = itr == _configOptions.end();
|
||||||
auto envVarName = GetEnvVarName(name);
|
auto envVarName = GetEnvVarName(name);
|
||||||
Optional<std::string> envVar = GetEnvFromCache(name, envVarName);
|
Optional<std::string> envVar = GetEnvFromCache(name, envVarName);
|
||||||
@@ -401,23 +554,23 @@ T ConfigMgr::GetValueDefault(std::string const& name, T const& def, bool showLog
|
|||||||
{
|
{
|
||||||
if (showLogs)
|
if (showLogs)
|
||||||
{
|
{
|
||||||
for (std::string s : _fatalConfigOptions)
|
bool isCritical = _criticalConfigOptions.find(name) != _criticalConfigOptions.end();
|
||||||
if (s == name)
|
ConfigSeverity severity = isCritical ? _policy.criticalOptionSeverity : _policy.missingOptionSeverity;
|
||||||
{
|
|
||||||
fatalConfig = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fatalConfig)
|
if (isCritical)
|
||||||
LOG_FATAL("server.loading", "> Config:\n\nFATAL ERROR: Missing property {} in config file {}, add \"{} = {}\" to this file or define '{}' as an environment variable\n\nYour server cannot start without this option!",
|
{
|
||||||
|
LogWithSeverity(severity, _filename,
|
||||||
|
"> Config:\n\nFATAL ERROR: Missing property {} in config file {}, add \"{} = {}\" to this file or define '{}' as an environment variable\n\nYour server cannot start without this option!",
|
||||||
name, _filename, name, Acore::ToString(def), envVarName);
|
name, _filename, name, Acore::ToString(def), envVarName);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::string configs = _filename;
|
std::string configs = _filename;
|
||||||
if (!_moduleConfigFiles.empty())
|
if (!_moduleConfigFiles.empty())
|
||||||
configs += " or module config";
|
configs += " or module config";
|
||||||
|
|
||||||
LOG_WARN("server.loading", "> Config: Missing property {} in config file {}, add \"{} = {}\" to this file or define '{}' as an environment variable.",
|
LogWithSeverity(severity, _filename,
|
||||||
|
"> Config: Missing property {} in config file {}, add \"{} = {}\" to this file or define '{}' as an environment variable.",
|
||||||
name, configs, name, def, envVarName);
|
name, configs, name, def, envVarName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -433,7 +586,8 @@ T ConfigMgr::GetValueDefault(std::string const& name, T const& def, bool showLog
|
|||||||
{
|
{
|
||||||
if (showLogs)
|
if (showLogs)
|
||||||
{
|
{
|
||||||
LOG_ERROR("server.loading", "> Config: Bad value defined for name '{}', going to use '{}' instead",
|
LogWithSeverity(_policy.valueErrorSeverity, _filename,
|
||||||
|
"> Config: Bad value defined for name '{}', going to use '{}' instead",
|
||||||
name, Acore::ToString(def));
|
name, Acore::ToString(def));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,7 +601,6 @@ template<>
|
|||||||
std::string ConfigMgr::GetValueDefault<std::string>(std::string const& name, std::string const& def, bool showLogs /*= true*/) const
|
std::string ConfigMgr::GetValueDefault<std::string>(std::string const& name, std::string const& def, bool showLogs /*= true*/) const
|
||||||
{
|
{
|
||||||
auto const& itr = _configOptions.find(name);
|
auto const& itr = _configOptions.find(name);
|
||||||
bool fatalConfig = false;
|
|
||||||
bool notFound = itr == _configOptions.end();
|
bool notFound = itr == _configOptions.end();
|
||||||
auto envVarName = GetEnvVarName(name);
|
auto envVarName = GetEnvVarName(name);
|
||||||
Optional<std::string> envVar = GetEnvFromCache(name, envVarName);
|
Optional<std::string> envVar = GetEnvFromCache(name, envVarName);
|
||||||
@@ -466,23 +619,23 @@ std::string ConfigMgr::GetValueDefault<std::string>(std::string const& name, std
|
|||||||
{
|
{
|
||||||
if (showLogs)
|
if (showLogs)
|
||||||
{
|
{
|
||||||
for (std::string s : _fatalConfigOptions)
|
bool isCritical = _criticalConfigOptions.find(name) != _criticalConfigOptions.end();
|
||||||
if (s == name)
|
ConfigSeverity severity = isCritical ? _policy.criticalOptionSeverity : _policy.missingOptionSeverity;
|
||||||
{
|
|
||||||
fatalConfig = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fatalConfig)
|
if (isCritical)
|
||||||
LOG_FATAL("server.loading", "> Config:\n\nFATAL ERROR: Missing property {} in config file {}, add \"{} = {}\" to this file or define '{}' as an environment variable.\n\nYour server cannot start without this option!",
|
{
|
||||||
|
LogWithSeverity(severity, _filename,
|
||||||
|
"> Config:\n\nFATAL ERROR: Missing property {} in config file {}, add \"{} = {}\" to this file or define '{}' as an environment variable.\n\nYour server cannot start without this option!",
|
||||||
name, _filename, name, def, envVarName);
|
name, _filename, name, def, envVarName);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::string configs = _filename;
|
std::string configs = _filename;
|
||||||
if (!_moduleConfigFiles.empty())
|
if (!_moduleConfigFiles.empty())
|
||||||
configs += " or module config";
|
configs += " or module config";
|
||||||
|
|
||||||
LOG_WARN("server.loading", "> Config: Missing property {} in config file {}, add \"{} = {}\" to this file or define '{}' as an environment variable.",
|
LogWithSeverity(severity, _filename,
|
||||||
|
"> Config: Missing property {} in config file {}, add \"{} = {}\" to this file or define '{}' as an environment variable.",
|
||||||
name, configs, name, def, envVarName);
|
name, configs, name, def, envVarName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -509,7 +662,8 @@ bool ConfigMgr::GetOption<bool>(std::string const& name, bool const& def, bool s
|
|||||||
{
|
{
|
||||||
if (showLogs)
|
if (showLogs)
|
||||||
{
|
{
|
||||||
LOG_ERROR("server.loading", "> Config: Bad value defined for name '{}', going to use '{}' instead",
|
LogWithSeverity(_policy.valueErrorSeverity, _filename,
|
||||||
|
"> Config: Bad value defined for name '{}', going to use '{}' instead",
|
||||||
name, def ? "true" : "false");
|
name, def ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -558,17 +712,27 @@ std::string const ConfigMgr::GetConfigPath()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigMgr::Configure(std::string const& initFileName, std::vector<std::string> args, std::string_view modulesConfigList /*= {}*/)
|
void ConfigMgr::Configure(std::string const& initFileName, std::vector<std::string> args, std::string_view modulesConfigList /*= {}*/, ConfigPolicy policy /*= {}*/)
|
||||||
{
|
{
|
||||||
_filename = initFileName;
|
_filename = initFileName;
|
||||||
_args = std::move(args);
|
_args = std::move(args);
|
||||||
|
_policy = policy;
|
||||||
|
|
||||||
|
if (char const* env = std::getenv("AC_CONFIG_POLICY"))
|
||||||
|
_policy = ApplyPolicyString(_policy, env);
|
||||||
|
|
||||||
|
_policy = ApplyPolicyFromArgs(_policy, _args);
|
||||||
|
|
||||||
|
_additonalFiles.clear();
|
||||||
|
_moduleConfigFiles.clear();
|
||||||
|
|
||||||
// Add modules config if exist
|
// Add modules config if exist
|
||||||
if (!modulesConfigList.empty())
|
if (!modulesConfigList.empty())
|
||||||
{
|
{
|
||||||
for (auto const& itr : Acore::Tokenize(modulesConfigList, ',', false))
|
for (auto const& itr : Acore::Tokenize(modulesConfigList, ',', false))
|
||||||
{
|
{
|
||||||
_additonalFiles.emplace_back(itr);
|
if (!itr.empty())
|
||||||
|
_additonalFiles.emplace_back(itr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,29 @@
|
|||||||
#ifndef CONFIG_H
|
#ifndef CONFIG_H
|
||||||
#define CONFIG_H
|
#define CONFIG_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
enum class ConfigSeverity : uint8_t
|
||||||
|
{
|
||||||
|
Skip,
|
||||||
|
Warn,
|
||||||
|
Error,
|
||||||
|
Fatal
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConfigPolicy
|
||||||
|
{
|
||||||
|
ConfigSeverity defaultSeverity = ConfigSeverity::Warn;
|
||||||
|
ConfigSeverity missingFileSeverity = ConfigSeverity::Error;
|
||||||
|
ConfigSeverity missingOptionSeverity = ConfigSeverity::Warn;
|
||||||
|
ConfigSeverity criticalOptionSeverity = ConfigSeverity::Fatal;
|
||||||
|
ConfigSeverity unknownOptionSeverity = ConfigSeverity::Error;
|
||||||
|
ConfigSeverity valueErrorSeverity = ConfigSeverity::Error;
|
||||||
|
};
|
||||||
|
|
||||||
class ConfigMgr
|
class ConfigMgr
|
||||||
{
|
{
|
||||||
ConfigMgr() = default;
|
ConfigMgr() = default;
|
||||||
@@ -32,7 +51,7 @@ class ConfigMgr
|
|||||||
public:
|
public:
|
||||||
bool LoadAppConfigs(bool isReload = false);
|
bool LoadAppConfigs(bool isReload = false);
|
||||||
bool LoadModulesConfigs(bool isReload = false, bool isNeedPrintInfo = true);
|
bool LoadModulesConfigs(bool isReload = false, bool isNeedPrintInfo = true);
|
||||||
void Configure(std::string const& initFileName, std::vector<std::string> args, std::string_view modulesConfigList = {});
|
void Configure(std::string const& initFileName, std::vector<std::string> args, std::string_view modulesConfigList = {}, ConfigPolicy policy = {});
|
||||||
|
|
||||||
static ConfigMgr* instance();
|
static ConfigMgr* instance();
|
||||||
|
|
||||||
|
|||||||
@@ -211,13 +211,16 @@ void Log::ReadLoggersFromConfig()
|
|||||||
AppenderConsole* appender = new AppenderConsole(NextAppenderId(), "Console", LOG_LEVEL_DEBUG, APPENDER_FLAGS_NONE, {});
|
AppenderConsole* appender = new AppenderConsole(NextAppenderId(), "Console", LOG_LEVEL_DEBUG, APPENDER_FLAGS_NONE, {});
|
||||||
appenders[appender->getId()].reset(appender);
|
appenders[appender->getId()].reset(appender);
|
||||||
|
|
||||||
Logger* rootLogger = new Logger(LOGGER_ROOT, LOG_LEVEL_ERROR);
|
Logger* rootLogger = new Logger(LOGGER_ROOT, LOG_LEVEL_WARN);
|
||||||
rootLogger->addAppender(appender->getId(), appender);
|
rootLogger->addAppender(appender->getId(), appender);
|
||||||
loggers[LOGGER_ROOT].reset(rootLogger);
|
loggers[LOGGER_ROOT].reset(rootLogger);
|
||||||
|
|
||||||
Logger* serverLogger = new Logger("server", LOG_LEVEL_INFO);
|
Logger* serverLogger = new Logger("server", LOG_LEVEL_INFO);
|
||||||
serverLogger->addAppender(appender->getId(), appender);
|
serverLogger->addAppender(appender->getId(), appender);
|
||||||
loggers["server"].reset(serverLogger);
|
loggers["server"].reset(serverLogger);
|
||||||
|
|
||||||
|
highestLogLevel = LOG_LEVEL_INFO;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -278,7 +278,8 @@ variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile)
|
|||||||
("help,h", "print usage message")
|
("help,h", "print usage message")
|
||||||
("version,v", "print version build info")
|
("version,v", "print version build info")
|
||||||
("dry-run,d", "Dry run")
|
("dry-run,d", "Dry run")
|
||||||
("config,c", value<fs::path>(&configFile)->default_value(fs::path(sConfigMgr->GetConfigPath() + std::string(_ACORE_REALM_CONFIG))), "use <arg> as configuration file");
|
("config,c", value<fs::path>(&configFile)->default_value(fs::path(sConfigMgr->GetConfigPath() + std::string(_ACORE_REALM_CONFIG))), "use <arg> as configuration file")
|
||||||
|
("config-policy", value<std::string>()->value_name("policy"), "override config severity policy (e.g. default=skip,critical_option=fatal)");
|
||||||
|
|
||||||
variables_map variablesMap;
|
variables_map variablesMap;
|
||||||
|
|
||||||
|
|||||||
@@ -423,7 +423,7 @@ bool StartDB()
|
|||||||
MySQL::Library_Init();
|
MySQL::Library_Init();
|
||||||
|
|
||||||
// Load databases
|
// Load databases
|
||||||
DatabaseLoader loader("server.worldserver", DatabaseLoader::DATABASE_NONE, AC_MODULES_LIST);
|
DatabaseLoader loader("server.worldserver", DatabaseLoader::DATABASE_MASK_ALL, AC_MODULES_LIST);
|
||||||
loader
|
loader
|
||||||
.AddDatabase(LoginDatabase, "Login")
|
.AddDatabase(LoginDatabase, "Login")
|
||||||
.AddDatabase(CharacterDatabase, "Character")
|
.AddDatabase(CharacterDatabase, "Character")
|
||||||
@@ -433,7 +433,7 @@ bool StartDB()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
///- Get the realm Id from the configuration file
|
///- Get the realm Id from the configuration file
|
||||||
realm.Id.Realm = sConfigMgr->GetOption<uint32>("RealmID", 0);
|
realm.Id.Realm = sConfigMgr->GetOption<uint32>("RealmID", 1);
|
||||||
if (!realm.Id.Realm)
|
if (!realm.Id.Realm)
|
||||||
{
|
{
|
||||||
LOG_ERROR("server.worldserver", "Realm ID not defined in configuration file");
|
LOG_ERROR("server.worldserver", "Realm ID not defined in configuration file");
|
||||||
@@ -710,7 +710,8 @@ variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, [
|
|||||||
("help,h", "print usage message")
|
("help,h", "print usage message")
|
||||||
("version,v", "print version build info")
|
("version,v", "print version build info")
|
||||||
("dry-run,d", "Dry run")
|
("dry-run,d", "Dry run")
|
||||||
("config,c", value<fs::path>(&configFile)->default_value(fs::path(sConfigMgr->GetConfigPath() + std::string(_ACORE_CORE_CONFIG))), "use <arg> as configuration file");
|
("config,c", value<fs::path>(&configFile)->default_value(fs::path(sConfigMgr->GetConfigPath() + std::string(_ACORE_CORE_CONFIG))), "use <arg> as configuration file")
|
||||||
|
("config-policy", value<std::string>()->value_name("policy"), "override config severity policy (e.g. default=skip,critical_option=fatal)");
|
||||||
|
|
||||||
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
|
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
|
||||||
options_description win("Windows platform specific options");
|
options_description win("Windows platform specific options");
|
||||||
|
|||||||
@@ -24,6 +24,24 @@
|
|||||||
#include <errmsg.h>
|
#include <errmsg.h>
|
||||||
#include <mysqld_error.h>
|
#include <mysqld_error.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <string_view>
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string const EMPTY_DATABASE_INFO;
|
||||||
|
std::string const LOGIN_DATABASE_INFO_DEFAULT = "127.0.0.1;3306;acore;acore;acore_auth";
|
||||||
|
std::string const WORLD_DATABASE_INFO_DEFAULT = "127.0.0.1;3306;acore;acore;acore_world";
|
||||||
|
std::string const CHARACTER_DATABASE_INFO_DEFAULT = "127.0.0.1;3306;acore;acore;acore_characters";
|
||||||
|
std::string const& GetDefaultDatabaseInfo(std::string_view name)
|
||||||
|
{
|
||||||
|
if (name == "Login")
|
||||||
|
return LOGIN_DATABASE_INFO_DEFAULT;
|
||||||
|
if (name == "World")
|
||||||
|
return WORLD_DATABASE_INFO_DEFAULT;
|
||||||
|
if (name == "Character")
|
||||||
|
return CHARACTER_DATABASE_INFO_DEFAULT;
|
||||||
|
return EMPTY_DATABASE_INFO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DatabaseLoader::DatabaseLoader(std::string const& logger, uint32 const defaultUpdateMask, std::string_view modulesList)
|
DatabaseLoader::DatabaseLoader(std::string const& logger, uint32 const defaultUpdateMask, std::string_view modulesList)
|
||||||
: _logger(logger),
|
: _logger(logger),
|
||||||
@@ -38,7 +56,8 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
|
|||||||
|
|
||||||
_open.push([this, name, updatesEnabledForThis, &pool]() -> bool
|
_open.push([this, name, updatesEnabledForThis, &pool]() -> bool
|
||||||
{
|
{
|
||||||
std::string const dbString = sConfigMgr->GetOption<std::string>(name + "DatabaseInfo", "");
|
std::string const& defaultDatabaseInfo = GetDefaultDatabaseInfo(name);
|
||||||
|
std::string const dbString = sConfigMgr->GetOption<std::string>(name + "DatabaseInfo", defaultDatabaseInfo);
|
||||||
if (dbString.empty())
|
if (dbString.empty())
|
||||||
{
|
{
|
||||||
LOG_ERROR(_logger, "Database {} not specified in configuration file!", name);
|
LOG_ERROR(_logger, "Database {} not specified in configuration file!", name);
|
||||||
|
|||||||
@@ -109,8 +109,8 @@ bool StartDB()
|
|||||||
|
|
||||||
DatabaseLoader loader =
|
DatabaseLoader loader =
|
||||||
modules.empty() ? DatabaseLoader("dbimport") :
|
modules.empty() ? DatabaseLoader("dbimport") :
|
||||||
(modules == "all") ? DatabaseLoader("dbimport", DatabaseLoader::DATABASE_NONE, AC_MODULES_LIST) :
|
(modules == "all") ? DatabaseLoader("dbimport", DatabaseLoader::DATABASE_MASK_ALL, AC_MODULES_LIST) :
|
||||||
DatabaseLoader("dbimport", DatabaseLoader::DATABASE_NONE, modules);
|
DatabaseLoader("dbimport", DatabaseLoader::DATABASE_MASK_ALL, modules);
|
||||||
|
|
||||||
loader
|
loader
|
||||||
.AddDatabase(LoginDatabase, "Login")
|
.AddDatabase(LoginDatabase, "Login")
|
||||||
@@ -140,7 +140,8 @@ variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile)
|
|||||||
("help,h", "print usage message")
|
("help,h", "print usage message")
|
||||||
("version,v", "print version build info")
|
("version,v", "print version build info")
|
||||||
("dry-run,d", "Dry run")
|
("dry-run,d", "Dry run")
|
||||||
("config,c", value<fs::path>(&configFile)->default_value(fs::path(sConfigMgr->GetConfigPath() + std::string(_ACORE_DB_IMPORT_CONFIG))), "use <arg> as configuration file");
|
("config,c", value<fs::path>(&configFile)->default_value(fs::path(sConfigMgr->GetConfigPath() + std::string(_ACORE_DB_IMPORT_CONFIG))), "use <arg> as configuration file")
|
||||||
|
("config-policy", value<std::string>()->value_name("policy"), "override config severity policy (e.g. default=skip,critical_option=fatal)");
|
||||||
|
|
||||||
variables_map variablesMap;
|
variables_map variablesMap;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user