From 26e783ada32df0c6d834c850080b5814f8f8ef2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Sun, 1 Sep 2024 22:26:14 +0200 Subject: [PATCH] compiled dbScriptAllDatabases using go compiler --- bin/dbImportProfile | 91 +- bin/dbQueryAllDatabases | 106 +- bin/dbScriptAllDatabases | 4152 +++++++++-------- .../binary-dbImportProfile.yaml | 11 +- .../testsData/dbImportProfile.help.txt | 14 +- .../binary-dbQueryAllDatabases.yaml | 18 +- .../testsData/dbQueryAllDatabases.help.txt | 22 +- .../binary-dbScriptAllDatabases.yaml | 89 + .../dbScriptAllDatabases-main.sh | 35 + .../dbScriptAllDatabases-options.sh | 105 + .../dbScriptAllDatabases.bats | 4 +- .../testsData/databaseSize.envProvided.sh | 0 .../testsData/databaseSize.result_db1 | 0 .../testsData/databaseSize.result_db2 | 0 .../testsData/dbScriptAllDatabases.help.txt | 122 + .../testsData/dbScriptAllDatabases.result | 0 .../testsData/dsn_local.env | 0 .../testsData/dsn_valid.env | 0 .../testsData/getUserDbList.result | 0 .../dbScriptAllDatabases}/testsData/parallel | 0 .../testsData/parallelDbScriptAllDatabases | 0 .../dbScriptAllDatabases}/testsData/pv | 0 .../dbScriptAllDatabases.options.tpl | 159 - .../dbScriptAllDatabases.sh | 64 - .../testsData/dbScriptAllDatabases.help.txt | 118 - .../testsData/getUserDbList.query | 1 - .../commandDefinitions/optionsFromDsn.yaml | 16 + .../commandDefinitions/optionsJobs.sh | 6 + .../commandDefinitions/optionsJobs.yaml | 12 + .../commandDefinitions/optionsProgressBar.sh | 7 - .../optionsProgressBar.yaml | 21 - 31 files changed, 2819 insertions(+), 2354 deletions(-) create mode 100644 src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml create mode 100755 src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-main.sh create mode 100755 src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/dbScriptAllDatabases.bats (93%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/databaseSize.envProvided.sh (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/databaseSize.result_db1 (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/databaseSize.result_db2 (100%) create mode 100644 src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/dbScriptAllDatabases.result (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/dsn_local.env (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/dsn_valid.env (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/getUserDbList.result (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/parallel (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/parallelDbScriptAllDatabases (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/pv (100%) delete mode 100644 src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.options.tpl delete mode 100755 src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.sh delete mode 100644 src/_binaries/DbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt delete mode 100644 src/_binaries/DbScriptAllDatabases/testsData/getUserDbList.query create mode 100644 src/_binaries/commandDefinitions/optionsFromDsn.yaml delete mode 100755 src/_binaries/commandDefinitions/optionsProgressBar.sh delete mode 100644 src/_binaries/commandDefinitions/optionsProgressBar.yaml diff --git a/bin/dbImportProfile b/bin/dbImportProfile index 5ffd591d..b6272ae4 100755 --- a/bin/dbImportProfile +++ b/bin/dbImportProfile @@ -1698,6 +1698,7 @@ beforeParseCallback() { # ------------------------------------------ # options variables initialization +declare optionFromDsn="" declare optionHelp="0" declare optionConfig="0" declare optionBashFrameworkConfig="" @@ -1713,7 +1714,6 @@ declare optionTheme="default" declare optionVersion="0" declare optionQuiet="0" declare optionProfile="" -declare optionFromDsn="default.remote" declare optionRatio="70" # arguments variables initialization declare fromDbName="" @@ -1721,6 +1721,9 @@ declare fromDbName="" dbImportProfileCommandParse() { Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionFromDsn="" + local -i options_parse_optionParsedCountOptionFromDsn + ((options_parse_optionParsedCountOptionFromDsn = 0)) || true optionHelp="0" local -i options_parse_optionParsedCountOptionHelp ((options_parse_optionParsedCountOptionHelp = 0)) || true @@ -1764,9 +1767,6 @@ dbImportProfileCommandParse() { optionProfile="" local -i options_parse_optionParsedCountOptionProfile ((options_parse_optionParsedCountOptionProfile = 0)) || true - optionFromDsn="default.remote" - local -i options_parse_optionParsedCountOptionFromDsn - ((options_parse_optionParsedCountOptionFromDsn = 0)) || true optionRatio="70" local -i options_parse_optionParsedCountOptionRatio ((options_parse_optionParsedCountOptionRatio = 0)) || true @@ -1783,6 +1783,25 @@ dbImportProfileCommandParse() { local argOptDefaultBehavior=0 case "${options_parse_arg}" in # Option 1/17 + # optionFromDsn alts --from-dsn|-f + # type: String min 0 max 1 + --from-dsn | -f) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionFromDsn >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionFromDsn)) + # shellcheck disable=SC2034 + optionFromDsn="$1" + ;; + + # Option 2/17 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1798,7 +1817,7 @@ dbImportProfileCommandParse() { ;; - # Option 2/17 + # Option 3/17 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1812,7 +1831,7 @@ dbImportProfileCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 3/17 + # Option 4/17 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1833,7 +1852,7 @@ dbImportProfileCommandParse() { ;; - # Option 4/17 + # Option 5/17 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1851,7 +1870,7 @@ dbImportProfileCommandParse() { ;; - # Option 5/17 + # Option 6/17 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1869,7 +1888,7 @@ dbImportProfileCommandParse() { ;; - # Option 6/17 + # Option 7/17 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1887,7 +1906,7 @@ dbImportProfileCommandParse() { ;; - # Option 7/17 + # Option 8/17 # optionEnvFiles alts --env-file # type: StringArray min 0 max -1 --env-file) @@ -1905,7 +1924,7 @@ dbImportProfileCommandParse() { ;; - # Option 8/17 + # Option 9/17 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1933,7 +1952,7 @@ dbImportProfileCommandParse() { ;; - # Option 9/17 + # Option 10/17 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1956,7 +1975,7 @@ dbImportProfileCommandParse() { ;; - # Option 10/17 + # Option 11/17 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1984,7 +2003,7 @@ dbImportProfileCommandParse() { ;; - # Option 11/17 + # Option 12/17 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -2002,7 +2021,7 @@ dbImportProfileCommandParse() { ;; - # Option 12/17 + # Option 13/17 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -2030,7 +2049,7 @@ dbImportProfileCommandParse() { ;; - # Option 13/17 + # Option 14/17 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -2046,7 +2065,7 @@ dbImportProfileCommandParse() { ;; - # Option 14/17 + # Option 15/17 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -2064,7 +2083,7 @@ dbImportProfileCommandParse() { ;; - # Option 15/17 + # Option 16/17 # optionProfile alts --profile|-p # type: String min 0 max 1 --profile | -p) @@ -2083,25 +2102,6 @@ dbImportProfileCommandParse() { optionProfile="$1" ;; - # Option 16/17 - # optionFromDsn alts --from-dsn|-f - # type: String min 0 max 1 - --from-dsn | -f) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - if ((options_parse_optionParsedCountOptionFromDsn >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionFromDsn)) - # shellcheck disable=SC2034 - optionFromDsn="$1" - ;; - # Option 17/17 # optionRatio alts --ratio|-r # type: String min 0 max 1 @@ -2213,7 +2213,7 @@ dbImportProfileCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--profile|-p ]" "[--from-dsn|-f ]" "[--ratio|-r ]" + optionsAltList=("[--from-dsn|-f ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--profile|-p ]" "[--ratio|-r ]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "dbImportProfile" "${optionsAltList[@]}" @@ -2232,6 +2232,13 @@ dbImportProfileCommandHelp() { # ------------------------------------------ # options section # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}SOURCE OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Target mysql server." + echo + + echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" @@ -2330,14 +2337,6 @@ dbImportProfileCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" - optionFromDsnHelpFunction - - - - Array::wrap2 ' ' 76 6 " Default value: " "default.remote" - echo - echo -e " ${__HELP_OPTION_COLOR}--ratio${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-r ${__HELP_NORMAL} {single}" optionRatioHelpFunction diff --git a/bin/dbQueryAllDatabases b/bin/dbQueryAllDatabases index 073f4a64..ecee877b 100755 --- a/bin/dbQueryAllDatabases +++ b/bin/dbQueryAllDatabases @@ -1644,6 +1644,12 @@ beforeParseCallback() { +declare -a PARALLEL_OPTIONS + +optionProgressBarCallback() { + PARALLEL_OPTIONS+=(--bar) +} + optionJobsCallback() { # shellcheck disable=SC2154 if ! [[ "${optionJobs}" =~ ^[0-9]+$ ]]; then @@ -1657,14 +1663,6 @@ optionJobsCallback() { -declare -a PARALLEL_OPTIONS - -optionProgressBarCallback() { - PARALLEL_OPTIONS+=(--bar) -} - - - optionVersionCallback() { # shellcheck disable=SC2154 echo "${SCRIPT_NAME} version ${versionNumber}" @@ -1787,6 +1785,7 @@ dbQueryAllDatabasesCommandCallback() { # options variables initialization declare optionJobs="1" declare optionProgressBar="0" +declare optionFromDsn="" declare optionHelp="0" declare optionConfig="0" declare optionBashFrameworkConfig="" @@ -1802,7 +1801,6 @@ declare optionTheme="default" declare optionVersion="0" declare optionQuiet="0" declare optionSeparator="|" -declare optionFromDsn="" # arguments variables initialization declare argQuery="" # @description parse command options and arguments for dbQueryAllDatabasesCommand @@ -1815,6 +1813,9 @@ dbQueryAllDatabasesCommandParse() { optionProgressBar="0" local -i options_parse_optionParsedCountOptionProgressBar ((options_parse_optionParsedCountOptionProgressBar = 0)) || true + optionFromDsn="" + local -i options_parse_optionParsedCountOptionFromDsn + ((options_parse_optionParsedCountOptionFromDsn = 0)) || true optionHelp="0" local -i options_parse_optionParsedCountOptionHelp ((options_parse_optionParsedCountOptionHelp = 0)) || true @@ -1858,9 +1859,6 @@ dbQueryAllDatabasesCommandParse() { optionSeparator="|" local -i options_parse_optionParsedCountOptionSeparator ((options_parse_optionParsedCountOptionSeparator = 0)) || true - optionFromDsn="" - local -i options_parse_optionParsedCountOptionFromDsn - ((options_parse_optionParsedCountOptionFromDsn = 0)) || true argQuery="" local -i options_parse_argParsedCountArgQuery @@ -1911,6 +1909,25 @@ dbQueryAllDatabasesCommandParse() { ;; # Option 3/18 + # optionFromDsn alts --from-dsn|-f + # type: String min 0 max 1 + --from-dsn | -f) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionFromDsn >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionFromDsn)) + # shellcheck disable=SC2034 + optionFromDsn="$1" + ;; + + # Option 4/18 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1926,7 +1943,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 4/18 + # Option 5/18 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1940,7 +1957,7 @@ dbQueryAllDatabasesCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 5/18 + # Option 6/18 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1961,7 +1978,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 6/18 + # Option 7/18 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1979,7 +1996,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 7/18 + # Option 8/18 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1997,7 +2014,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 8/18 + # Option 9/18 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -2015,7 +2032,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 9/18 + # Option 10/18 # optionEnvFiles alts --env-file # type: StringArray min 0 max -1 --env-file) @@ -2033,7 +2050,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 10/18 + # Option 11/18 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -2061,7 +2078,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 11/18 + # Option 12/18 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -2084,7 +2101,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 12/18 + # Option 13/18 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -2112,7 +2129,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 13/18 + # Option 14/18 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -2130,7 +2147,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 14/18 + # Option 15/18 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -2158,7 +2175,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 15/18 + # Option 16/18 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -2174,7 +2191,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 16/18 + # Option 17/18 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -2192,7 +2209,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 17/18 + # Option 18/18 # optionSeparator alts --separator|-s # type: String min 0 max 1 --separator | -s) @@ -2213,25 +2230,6 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 18/18 - # optionFromDsn alts --from-dsn|-f - # type: String min 0 max 1 - --from-dsn | -f) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - if ((options_parse_optionParsedCountOptionFromDsn >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionFromDsn)) - # shellcheck disable=SC2034 - optionFromDsn="$1" - ;; - -*) if [[ "${argOptDefaultBehavior}" = "0" ]]; then Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" @@ -2329,7 +2327,7 @@ dbQueryAllDatabasesCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--jobs|-j ]" "[--bar|-b]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--separator|-s ]" "[--from-dsn|-f ]" + optionsAltList=("[--jobs|-j ]" "[--bar|-b]" "[--from-dsn|-f ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--separator|-s ]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "dbQueryAllDatabases" "${optionsAltList[@]}" @@ -2357,13 +2355,19 @@ dbQueryAllDatabasesCommandHelp() { Array::wrap2 ' ' 76 6 " Default value: " "1" echo - echo - echo -e "${__HELP_TITLE_COLOR}PROGRESS BAR OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--bar${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-b${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Show progress as a progress bar. In the bar is shown: % of jobs completed," "estimated seconds left, and number of jobs started." "" echo + echo + echo -e "${__HELP_TITLE_COLOR}SOURCE OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Target mysql server." + echo + + echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" @@ -2463,12 +2467,6 @@ dbQueryAllDatabasesCommandHelp() { Array::wrap2 ' ' 76 6 " Default value: " "|" echo - - echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Target mysql server." - echo - - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/bin/dbScriptAllDatabases b/bin/dbScriptAllDatabases index ea2a17f3..3fa881ac 100755 --- a/bin/dbScriptAllDatabases +++ b/bin/dbScriptAllDatabases @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/dbScriptAllDatabases -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,1994 +138,2458 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. +# +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" 0)); do + arg="$1" + shift || true + + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi + + ((argLength = ${#arg})) || true + + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine + fi + continue + fi + + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine + fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 fi - Log::logWarning "$1" + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logError "$1" + +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break + fi + done + return "${handlePipelineFailure_resultingStatusCode}" } -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color -# -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" - fi - case "${theme}" in - default | default-force) - theme="default" + +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ + "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ + "755" + +declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" + + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${embed_file_bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } +} + + +# @description convert base64 encoded back to target file +# if target file is executable prepend dir of target +# file to PATH to make binary available everywhere +# it is advised to include in the path of the target file +# the md5sum of the binFile +# +# @arg $1 targetFile:String the file to write +# @arg $2 binFileBase64:String the base64 encoded file +# @arg $3 fileMode:String the chmod to set on the file +# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable +Compiler::Embed::extractFileFromBase64() { + local targetFile="$1" + local binFileBase64="$2" + local fileMode="${3:-+x}" + local targetDir="${targetFile%/*}" + + if [[ ! -f "${targetFile}" ]]; then + if [[ ! -d "${targetDir}" ]]; then + mkdir -p "${targetDir}" + fi + base64 -d >"${targetFile}" <<<"${binFileBase64}" + chmod "${fileMode}" "${targetFile}" + fi + + if [[ -x "${targetFile}" ]]; then + Env::pathPrepend "${targetDir}" + fi +} + + +# @description get absolute conf file from specified conf folder deduced using these rules +# * from absolute file (ignores and ) +# * relative to where script is executed (ignores and ) +# * from home/.bash-tools/ +# * from framework conf/ +# +# @arg $1 confFolder:String the directory name (not the path) to list +# @arg $2 conf:String file to use without extension +# @arg $3 extension:String the extension (.sh by default) +# +# @stdout absolute conf filename +# @exitcode 1 if file is not found in any location +Conf::getAbsoluteFile() { + local confFolder="$1" + local conf="$2" + local extension="${3-.sh}" + if [[ -n "${extension}" && "${extension:0:1}" != "." ]]; then + extension=".${extension}" + fi + + testAbs() { + local result + result="$(realpath -e "$1" 2>/dev/null)" + # shellcheck disable=SC2181 + if [[ "$?" = "0" && -f "${result}" ]]; then + echo "${result}" + return 0 + fi + return 1 + } + + # conf is absolute file (including extension) + testAbs "${confFolder}${extension}" && return 0 + # conf is absolute file + testAbs "${confFolder}" && return 0 + # conf is absolute file (including extension) + testAbs "${conf}${extension}" && return 0 + # conf is absolute file + testAbs "${conf}" && return 0 + + # relative to where script is executed (including extension) + if [[ -n "${CURRENT_DIR+xxx}" ]]; then + testAbs "$(File::concatenatePath "${CURRENT_DIR}" "${confFolder}")/${conf}${extension}" && return 0 + fi + # from home/.bash-tools/ + testAbs "$(File::concatenatePath "${HOME}/.bash-tools" "${confFolder}")/${conf}${extension}" && return 0 + + if [[ -n "${FRAMEWORK_ROOT_DIR+xxx}" ]]; then + # from framework conf/ (including extension) + testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}${extension}" && return 0 + + # from framework conf/ + testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}" && return 0 + fi + + # file not found + Log::displayError "conf file '${conf}' not found" + + return 1 +} + + +# @description list the conf files list available in bash-tools/conf/ folder +# and those overridden in ${HOME}/.bash-tools/ folder +# +# @arg $1 confFolder:String the directory name (not the path) to list +# @arg $2 extension:String the extension (.sh by default) +# @arg $3 indentStr:String the indentation (' - ' by default) can be any string compatible with sed not containing any / +# +# @stdout list of files without extension/directory +# @example text +# - default.local +# - default.remote +# - localhost-root +Conf::getMergedList() { + local confFolder="$1" + local extension="${2-sh}" + local indentStr="${3- - }" + + local DEFAULT_CONF_DIR="${FRAMEWORK_ROOT_DIR}/conf/${confFolder}" + local HOME_CONF_DIR="${HOME}/.bash-tools/${confFolder}" + + ( + if [[ -d "${DEFAULT_CONF_DIR}" ]]; then + Conf::list "${DEFAULT_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" + fi + if [[ -d "${HOME_CONF_DIR}" ]]; then + Conf::list "${HOME_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" + fi + ) | sort | uniq +} + + +# @description list files of dir with given extension and display it as a list one by line +# +# @arg $1 dir:String the directory to list +# @arg $2 prefix:String the profile file prefix (default: "") +# @arg $3 ext:String the extension +# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') +# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') +# @stdout list of files without extension/directory +# @example text +# - default.local +# - default.remote +# - localhost-root +# @exitcode 1 if directory does not exists +Conf::list() { + local dir="$1" + local prefix="${2:-}" + local ext="${3}" + local findOptions="${4--type f}" + local indentStr="${5- - }" + + if [[ ! -d "${dir}" ]]; then + Log::displayError "Directory ${dir} does not exist" + fi + if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then + ext=".${ext}" + fi + ( + # shellcheck disable=SC2086 + cd "${dir}" && + find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | + sed -E "s#^\./${prefix}##g" | + sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" + ) +} + + +# @description check if dsn file has all the mandatory variables set +# Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT +# +# @arg $1 dsnFileName:String dsn absolute filename +# @set HOSTNAME loaded from dsn file +# @set PORT loaded from dsn file +# @set USER loaded from dsn file +# @set PASSWORD loaded from dsn file +# @exitcode 0 on valid file +# @exitcode 1 if one of the properties of the conf file is invalid or if file not found +# @stderr log output if error found in conf file +Database::checkDsnFile() { + local dsnFileName="$1" + if [[ ! -f "${dsnFileName}" ]]; then + Log::displayError "dsn file ${dsnFileName} not found" + return 1 + fi + + ( + unset HOSTNAME PORT PASSWORD USER + # shellcheck source=/src/Database/testsData/dsn_valid.env + source "${dsnFileName}" + if [[ -z ${HOSTNAME+x} ]]; then + Log::displayError "dsn file ${dsnFileName} : HOSTNAME not provided" + return 1 + fi + if [[ -z "${HOSTNAME}" ]]; then + Log::displayWarning "dsn file ${dsnFileName} : HOSTNAME value not provided" + fi + if [[ "${HOSTNAME}" = "localhost" ]]; then + Log::displayWarning "dsn file ${dsnFileName} : check that HOSTNAME should not be 127.0.0.1 instead of localhost" + fi + if [[ -z "${PORT+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : PORT not provided" + return 1 + fi + if ! [[ ${PORT} =~ ^[0-9]+$ ]]; then + Log::displayError "dsn file ${dsnFileName} : PORT invalid" + return 1 + fi + if [[ -z "${USER+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : USER not provided" + return 1 + fi + if [[ -z "${PASSWORD+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : PASSWORD not provided" + return 1 + fi + ) +} + + +# @description databases's list of given mysql server +# +# @example text +# - information_schema +# - mysql +# - performance_schema +# - sys +# +# @arg $1 instanceUserDbList:&Map (passed by reference) database instance to use +# @stdout the list of db except mysql admin ones (see example) +# @exitcode * the query exit code +Database::getUserDbList() { + # shellcheck disable=SC2034 + local -n instanceUserDbList=$1 + # shellcheck disable=SC2016 + local sql='SELECT `schema_name` from INFORMATION_SCHEMA.SCHEMATA WHERE `schema_name` NOT IN("information_schema", "mysql", "performance_schema", "sys")' + Database::query instanceUserDbList "${sql}" +} + + +# @description create a new db instance +# Returns immediately if the instance is already initialized +# +# @arg $1 instanceNewInstance:&Map (passed by reference) database instance to use +# @arg $2 dsn:String dsn profile - load the dsn.env profile deduced using rules defined in Conf::getAbsoluteFile +# +# @example +# declare -Agx dbInstance +# Database::newInstance dbInstance "default.local" +# +# @exitcode 1 if dns file not able to loaded +Database::newInstance() { + local -n instanceNewInstance=$1 + local dsn="$2" + local DSN_FILE + + if [[ -v instanceNewInstance['INITIALIZED'] && "${instanceNewInstance['INITIALIZED']:-0}" == "1" ]]; then + return + fi + + # final auth file generated from dns file + instanceNewInstance['AUTH_FILE']="" + instanceNewInstance['DSN_FILE']="" + + # check dsn file + DSN_FILE="$(Conf::getAbsoluteFile "dsn" "${dsn}" "env")" || return 1 + Database::checkDsnFile "${DSN_FILE}" || return 1 + instanceNewInstance['DSN_FILE']="${DSN_FILE}" + + # shellcheck source=/src/Database/testsData/dsn_valid.env + source "${instanceNewInstance['DSN_FILE']}" + + instanceNewInstance['USER']="${USER}" + instanceNewInstance['PASSWORD']="${PASSWORD}" + instanceNewInstance['HOSTNAME']="${HOSTNAME}" + instanceNewInstance['PORT']="${PORT}" + + # generate authFile for easy authentication + instanceNewInstance['AUTH_FILE']=$(mktemp -p "${TMPDIR:-/tmp}" -t "mysql.XXXXXXXXXXXX") + ( + echo "[client]" + echo "user = ${USER}" + echo "password = ${PASSWORD}" + echo "host = ${HOSTNAME}" + echo "port = ${PORT}" + ) >"${instanceNewInstance['AUTH_FILE']}" + + # some of those values can be overridden using the dsn file + # SKIP_COLUMN_NAMES enabled by default + instanceNewInstance['SKIP_COLUMN_NAMES']="${SKIP_COLUMN_NAMES:-1}" + instanceNewInstance['SSL_OPTIONS']="${MYSQL_SSL_OPTIONS:---ssl-mode=DISABLED}" + instanceNewInstance['QUERY_OPTIONS']="${MYSQL_QUERY_OPTIONS:---batch --raw --default-character-set=utf8}" + instanceNewInstance['DUMP_OPTIONS']="${MYSQL_DUMP_OPTIONS:---default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 ${instanceNewInstance['SSL_OPTIONS']}}" + instanceNewInstance['DB_IMPORT_OPTIONS']="${DB_IMPORT_OPTIONS:---connect-timeout=5 --batch --raw --default-character-set=utf8}" + + instanceNewInstance['INITIALIZED']=1 +} + + +# @description mysql query on a given db +# @warning could use QUERY_OPTIONS variable from dsn if defined +# @example +# cat file.sql | Database::query ... +# @arg $1 instanceQuery:&Map (passed by reference) database instance to use +# @arg $2 sqlQuery:String (optional) sql query or sql file to execute. if not provided or empty, the command can be piped +# @arg $3 dbName:String (optional) the db name +# +# @exitcode mysql command status code +Database::query() { + local -n instanceQuery=$1 + local -a mysqlCommand=() + local -a queryOptions + + mysqlCommand+=(mysql) + mysqlCommand+=("--defaults-extra-file=${instanceQuery['AUTH_FILE']}") + IFS=' ' read -r -a queryOptions <<<"${instanceQuery['QUERY_OPTIONS']}" + mysqlCommand+=("${queryOptions[@]}") + if [[ "${instanceQuery['SKIP_COLUMN_NAMES']}" = "1" ]]; then + mysqlCommand+=("-s" "--skip-column-names") + fi + # add optional db name + if [[ -n "${3+x}" ]]; then + mysqlCommand+=("$3") + fi + # add optional sql query + if [[ -n "${2+x}" && -n "$2" && ! -f "$2" ]]; then + mysqlCommand+=("-e") + mysqlCommand+=("$2") + fi + Log::displayDebug "$(printf "execute command: '%s'" "${mysqlCommand[*]}")" + + if [[ -f "$2" ]]; then + "${mysqlCommand[@]}" <"$2" + else + "${mysqlCommand[@]}" + fi +} + + +# @description set the general options to use on mysql command to query the database +# Differs than setOptions in the way that these options could change each time +# +# @arg $1 instanceSetQueryOptions:&Map (passed by reference) database instance to use +# @arg $2 optionsList:String query options list +Database::setQueryOptions() { + local -n instanceSetQueryOptions=$1 + # shellcheck disable=SC2034 + instanceSetQueryOptions['QUERY_OPTIONS']="$2" +} + + +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 + fi + local -i failures=0 + echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" +} + + +# @description prepend directories to the PATH environment variable +# @arg $@ args:String[] list of directories to prepend +# @set PATH update PATH with the directories prepended +Env::pathPrepend() { + local arg + for arg in "$@"; do + if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then + PATH="$(realpath "${arg}"):${PATH}" + fi + done +} + + +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load +# @stderr diagnostics information is displayed +# shellcheck disable=SC2120 +Env::requireLoad() { + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") + fi + local localFrameworkConfigFile + localFrameworkConfigFile="$(pwd)/.framework-config" + if [[ -f "${localFrameworkConfigFile}" ]]; then + configFiles+=("${localFrameworkConfigFile}") + fi + if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then + configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + fi + configFiles+=("${optionEnvFiles[@]}") + configFiles+=("${defaultFiles[@]}") + + for file in "${configFiles[@]}"; do + # shellcheck source=/.framework-config + CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { + Log::displayError "while loading config file: ${file}" + return 1 + } + done +} + + +# @description concatenate 2 paths and ensure the path is correct using realpath -m +# @arg $1 basePath:String +# @arg $2 subPath:String +File::concatenatePath() { + + if [[ "${REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Linux::requireRealpathCommand has not been loaded" + exit 1 + fi + + local basePath="$1" + local subPath="$2" + local fullPath="${basePath:+${basePath}/}${subPath}" + + realpath -m "${fullPath}" 2>/dev/null +} + + +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" +} + + +# @description ensure running user is not root +# @exitcode 1 if current user is root +# @stderr diagnostics information is displayed +Linux::requireExecutedAsUser() { + if [[ "$(id -u)" = "0" ]]; then + Log::fatal "this script should be executed as normal user" + fi +} + + +# @description ensure command realpath is available +# @exitcode 1 if realpath command not available +# @stderr diagnostics information is displayed +Linux::requireRealpathCommand() { + export REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 + + Assert::commandExists realpath +} + + +# @description ensure command tar is available +# @exitcode 1 if tar command not available +# @stderr diagnostics information is displayed +Linux::requireTarCommand() { + Assert::commandExists tar +} + + +declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR +FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" +LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" +LOG_LAST_LOG_DATE_INIT=1 +LOG_LAST_DURATION_STR="" + +# @description compute duration since last call to this function +# the result is set in following env variables. +# in ss.sss (seconds followed by milliseconds precision 3 decimals) +# @noargs +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @set LOG_LAST_LOG_DATE_INIT int (default 1) set to 0 at first call, allows to detect reference log +# @set LOG_LAST_DURATION_STR String the last duration displayed +# @set LOG_LAST_LOG_DATE String the last log date that will be used to compute next diff +Log::computeDuration() { + if ((${DISPLAY_DURATION:-0} == 1)); then + local -i duration=0 + local -i delta=0 + local -i currentLogDate + currentLogDate="${EPOCHREALTIME/[^0-9]/}" + if ((LOG_LAST_LOG_DATE_INIT == 1)); then + LOG_LAST_LOG_DATE_INIT=0 + LOG_LAST_DURATION_STR="Ref" + else + duration=$(((currentLogDate - FIRST_LOG_DATE) / 1000000)) + delta=$(((currentLogDate - LOG_LAST_LOG_DATE) / 1000000)) + LOG_LAST_DURATION_STR="${duration}s/+${delta}s" + fi + LOG_LAST_LOG_DATE="${currentLogDate}" + # shellcheck disable=SC2034 + local microSeconds="${EPOCHREALTIME#*.}" + LOG_LAST_DURATION_STR="$(printf '%(%T)T.%03.0f\n' "${EPOCHSECONDS}" "${microSeconds:0:3}")(${LOG_LAST_DURATION_STR}) - " + else + # shellcheck disable=SC2034 + LOG_LAST_DURATION_STR="" + fi +} + + +# @description Display message using debug color (gray) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logDebug "$1" +} + + +# @description Display message using error color (red) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} + + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} + + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logWarning "$1" +} + + +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logDebug() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then + Log::logMessage "${2:-DEBUG}" "$1" + fi +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logError() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_ERROR)); then + Log::logMessage "${2:-ERROR}" "$1" + fi +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logFatal() { + Log::logMessage "${2:-FATAL}" "$1" +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + +# @description Internal: common log message +# @example text +# [date]|[levelMsg]|message +# +# @example text +# 2020-01-19 19:20:21|ERROR |log error +# 2020-01-19 19:20:21|SKIPPED|log skipped +# +# @arg $1 levelMsg:String message's level description (eg: STATUS, ERROR, ...) +# @arg $2 msg:String the message to display +# @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty +# @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages +# @stderr diagnostics information is displayed +Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + + local levelMsg="$1" + local msg="$2" + local date + + if [[ -n "${BASH_FRAMEWORK_LOG_FILE}" ]] && ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + date="$(date '+%Y-%m-%d %H:%M:%S')" + touch "${BASH_FRAMEWORK_LOG_FILE}" + printf "%s|%7s|%s\n" "${date}" "${levelMsg}" "${msg}" >>"${BASH_FRAMEWORK_LOG_FILE}" + fi +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + +# @description To be called before logging in the log file +# @arg $1 file:string log file name +# @arg $2 maxLogFilesCount:int maximum number of log files +Log::rotate() { + local file="$1" + local maxLogFilesCount="${2:-5}" + + if [[ ! -f "${file}" ]]; then + Log::displayDebug "Log file ${file} doesn't exist yet" + return 0 + fi + local i + for ((i = maxLogFilesCount - 1; i > 0; i--)); do + Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" + mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true + done + if cp "${file}" "${file}.1" &>/dev/null; then + echo >"${file}" # reset log file + Log::displayInfo "Log rotation ${file} to ${file}.1" + fi +} + + +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) + fi + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo +} + + +# @description load color theme +# @noargs +# @env BASH_FRAMEWORK_THEME String theme to use +# @env LOAD_THEME int 0 to avoid loading theme +# @exitcode 0 always successful +UI::requireTheme() { + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + + if [[ "${LOAD_THEME:-1}" = "1" ]]; then + UI::theme "${BASH_FRAMEWORK_THEME-default}" + fi +} + + +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi +} + + +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" + + Assert::commandExists "${commandName}" "${help}" || return 2 + + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + return 1 + fi + return 0 + } + +} + + +# @description compare 2 version numbers +# @arg $1 version1:String version 1 +# @arg $2 version2:String version 2 +# @exitcode 0 if equal +# @exitcode 1 if version1 > version2 +# @exitcode 2 else +Version::compare() { + if [[ "$1" = "$2" ]]; then + return 0 + fi + local IFS=. + # shellcheck disable=2206 + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 + done + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]+unset}" ]] || [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 + fi + done + return 0 +} + + +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" +} +# FUNCTIONS + + +declare -a BASH_FRAMEWORK_ARGV_FILTERED=() + +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +copyrightCallback() { + if [[ -z "${copyrightBeginYear}" ]]; then + copyrightBeginYear="$(date +%Y)" + fi + echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" +} + +# shellcheck disable=SC2317 # if function is overridden +updateArgListInfoVerboseCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(--verbose) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListDebugVerboseCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(-vv) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListTraceVerboseCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(-vvv) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListEnvFileCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListLogLevelCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListDisplayLevelCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListNoColorCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(--no-color) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListThemeCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListQuietCallback() { :; } + +# shellcheck disable=SC2317 # if function is overridden +optionHelpCallback() { + Log::displayError "optionHelpCallback needs to be overridden" + exit 0 +} + +# shellcheck disable=SC2317 # if function is overridden +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" + exit 0 +} + +# shellcheck disable=SC2317 # if function is overridden +optionEnvFileCallback() { + local envFile="$2" + Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" + if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" + exit 1 + fi +} + +# shellcheck disable=SC2317 # if function is overridden +optionInfoVerboseCallback() { + BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' + BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionDebugVerboseCallback() { + BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' + BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionTraceVerboseCallback() { + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' + BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" +} + +getLevel() { + local levelName="$1" + case "${levelName^^}" in + OFF) + echo "${__LEVEL_OFF}" + ;; + ERR | ERROR) + echo "${__LEVEL_ERROR}" + ;; + WARN | WARNING) + echo "${__LEVEL_WARNING}" + ;; + INFO) + echo "${__LEVEL_INFO}" + ;; + DEBUG | TRACE) + echo "${__LEVEL_DEBUG}" ;; - noColor) ;; *) - Log::fatal "invalid theme provided" + Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" + return 1 ;; esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' +} + +getVerboseLevel() { + local levelName="$1" + case "${levelName^^}" in + OFF) + echo "${__VERBOSE_LEVEL_OFF}" + ;; + ERR | ERROR | WARN | WARNING | INFO) + echo "${__VERBOSE_LEVEL_INFO}" + ;; + DEBUG) + echo "${__VERBOSE_LEVEL_DEBUG}" + ;; + TRACE) + echo "${__VERBOSE_LEVEL_TRACE}" + ;; + *) + Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" + return 1 + ;; + esac +} + +# shellcheck disable=SC2317 # if function is overridden +optionDisplayLevelCallback() { + local level="$2" + local logLevel verboseLevel + logLevel="$(getLevel "${level}")" + verboseLevel="$(getVerboseLevel "${level}")" + BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionLogLevelCallback() { + local level="$2" + local logLevel verboseLevel + logLevel="$(getLevel "${level}")" + verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionLogFileCallback() { + local logFile="$2" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionQuietCallback() { + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionNoColorCallback() { + UI::theme "noColor" +} + +# shellcheck disable=SC2317 # if function is overridden +optionThemeCallback() { + UI::theme "$2" +} + +displayConfig() { + echo "Config" + UI::drawLine "-" + local var + while read -r var; do + printf '%-40s = %s\n' "${var}" "$(declare -p "${var}" | sed -E -e 's/^[^=]+=(.*)/\1/')" + done < <(typeset -p | awk 'match($3, "^(BASH_FRAMEWORK_[^=]+)=", m) { print m[1] }' | sort) + exit 0 +} + +optionBashFrameworkConfigCallback() { + if [[ ! -f "$2" ]]; then + Log::fatal "Command ${SCRIPT_NAME} - Bash framework config file '$2' does not exists" + fi +} + +defaultFrameworkConfig="$( + cat <<'EOF' + +# copied from src/_includes/.framework-config.default +# shellcheck disable=SC2034 + +REAL_SCRIPT_FILE="${REAL_SCRIPT_FILE:-$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")}" +FRAMEWORK_ROOT_DIR="${FRAMEWORK_ROOT_DIR:-${REAL_SCRIPT_FILE%/*/*}}" +FRAMEWORK_SRC_DIR="${FRAMEWORK_SRC_DIR:-${FRAMEWORK_ROOT_DIR}/src}" +FRAMEWORK_BIN_DIR="${FRAMEWORK_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/bin}" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_VENDOR_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/vendor/bin}" + +# describe the functions that will be skipped from being imported +FRAMEWORK_FUNCTIONS_IGNORE_REGEXP="${FRAMEWORK_FUNCTIONS_IGNORE_REGEXP:-^(Namespace::functions|Functions::myFunction|Namespace::requireSomething|Acquire::ForceIPv4)$}" +# describe the files that do not contain function to be imported +NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/_binaries|^src/_includes|^src/batsHeaders.sh$|^src/_standalone)}" +# describe the files that are allowed to not have an associated bats file +BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" +# describe the files that are allowed to not have a function matching the filename +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" +# Source directories +if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then + FRAMEWORK_SRC_DIRS=( + "${FRAMEWORK_ROOT_DIR}/src" + ) +fi + +# export here all the variables that will be used in your templates +export REPOSITORY_URL="${REPOSITORY_URL:-https://github.com/fchastanet/bash-tools-framework}" + +BASH_FRAMEWORK_THEME="${BASH_FRAMEWORK_THEME:-default}" +BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" +BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" +BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" +BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + +EOF +)" + +overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" + +commandOptionParseFinished() { + # load default template framework config + defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" + local -a files=("${defaultEnvFile}") + if [[ -f "${envFile}" ]]; then + files+=("${envFile}") + fi + # shellcheck disable=SC2154 + if [[ -f "${optionBashFrameworkConfig}" ]]; then + files+=("${optionBashFrameworkConfig}") + fi + files+=("${overrideEnvFile}") + Env::requireLoad "${files[@]}" + Log::requireLoad + # shellcheck disable=SC2154 + if [[ "${optionConfig}" = "1" ]]; then + displayConfig fi } -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 - fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") - echo + + +beforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + + + +declare SCRIPTS_DIR +declare HOME_SCRIPTS_DIR +# shellcheck disable=SC2034 +declare versionNumber="2.0" +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare defaultFromDsn="default.remote" +# shellcheck disable=SC2034 +declare outputDirectory="${HOME}/.bash-tools/output" + +beforeParseCallback() { + Assert::commandExists mysql "sudo apt-get install -y mysql-client" + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" + Assert::commandExists parallel "sudo apt-get install -y parallel" + + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad + Linux::requireExecutedAsUser + Linux::requireRealpathCommand +} + +initConf() { + # shellcheck disable=SC2034 + SCRIPTS_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbScripts" + HOME_SCRIPTS_DIR="${HOME}/.bash-tools/dbScripts" + Db::checkRequirements } -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 +optionHelpCallback() { + dbScriptAllDatabasesCommandHelp + exit 0 } -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" +longDescriptionFunction() { + local dsnList scriptsList + dsnList="$(Conf::getMergedList "dsn" "env")" + scriptsList="$(Conf::getMergedList "dbScripts" "sh")" + + echo -e "${__HELP_TITLE}NOTE:${__HELP_NORMAL}" + echo -e "the use of output, log-format, verbose options highly depends on the script used" + echo + echo -e "${__HELP_TITLE}LIST OF AVAILABLE DSN:${__HELP_NORMAL}" + echo -e "${dsnList}" + echo + echo -e "${__HELP_TITLE}DEFAULT SCRIPTS DIRECTORY:${__HELP_NORMAL}" + echo -e "${SCRIPTS_DIR-configuration error}" + echo + echo -e "${__HELP_TITLE}USER SCRIPTS DIRECTORY:${__HELP_NORMAL}" + echo -e "${HOME_SCRIPTS_DIR-configuration error}" + echo -e "Allows to override queries defined in 'Default scripts directory'" + echo + echo -e "${__HELP_TITLE}LIST OF AVAILABLE SCRIPTS:${__HELP_NORMAL}" + echo -e "${scriptsList}" + echo + echo -e "${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL} script conf/dbScripts/extractData.sh" + echo -e " executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using" + echo -e " ${__HELP_EXAMPLE}$0 -j 10 extractData databaseSize${__HELP_NORMAL}" + echo + echo -e " executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages)" + echo -e " ${__HELP_EXAMPLE}$0 -j 10 --log-format none extractData databaseSize${__HELP_NORMAL}" + echo + echo -e " use --verbose to get some debug information" + echo -e " ${__HELP_EXAMPLE}$0 -j 10 --log-format none --verbose extractData databaseSize${__HELP_NORMAL}" + echo + echo -e "${__HELP_TITLE}USE CASES:${__HELP_NORMAL}" + echo -e " you can use this script in order to check that each db model conforms with your ORM schema" + echo -e " simply create a new script in conf/dbQueries that will call your orm schema checker" + echo + echo -e " update multiple db at once (simple to complex update script)" } -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") - fi - local localFrameworkConfigFile - localFrameworkConfigFile="$(pwd)/.framework-config" - if [[ -f "${localFrameworkConfigFile}" ]]; then - configFiles+=("${localFrameworkConfigFile}") +outputDirectoryCallback() { + if [[ "${optionOutputDir:0:1}" != "/" ]]; then + # relative path + optionOutputDir="${PWD}/${optionOutputDir}" fi - if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then - configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + mkdir -p "${optionOutputDir}" || Log::fatal "unable to create directory ${optionOutputDir}" + if [[ ! -d "${optionOutputDir}" || ! -w "${optionOutputDir}" ]]; then + Log::fatal "output dir is not correct or not writable" fi - configFiles+=("${optionEnvFiles[@]}") - configFiles+=("${defaultFiles[@]}") +} - for file in "${configFiles[@]}"; do - # shellcheck source=/.framework-config - CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { - Log::displayError "while loading config file: ${file}" - return 1 +argScriptToExecuteCallback() { + if [[ ! -f "${argScriptToExecute}" ]]; then + declare scriptAbsoluteFile + scriptAbsoluteFile="$(Conf::getAbsoluteFile "dbScripts" "${argScriptToExecute}" "sh")" && { + argScriptToExecute="${scriptAbsoluteFile}" + if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + Log::displayInfo "Using script file ${scriptAbsoluteFile}" + fi } - done + fi } -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL +dbScriptAllDatabasesCommandCallback() { + if [[ -z "${optionFromDsn}" ]]; then + # default value for FROM_DSN if from-aws not set + optionFromDsn="${defaultFromDsn}" fi +} - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" - fi - fi + +declare -a PARALLEL_OPTIONS + +optionProgressBarCallback() { + PARALLEL_OPTIONS+=(--bar) } -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. -# -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" 0)); do - arg="$1" - shift || true +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" + Db::checkRequirements + exit 0 +} - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine + +# ------------------------------------------ +# Command dbScriptAllDatabasesCommand +# ------------------------------------------ + +# options variables initialization +declare optionJobs="1" +declare optionProgressBar="0" +declare optionFromDsn="" +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +declare -a optionDatabases=() +declare optionOutputDir="" +declare optionLogFormat="none" +# arguments variables initialization +declare argScriptToExecute="" +declare -a scriptArguments=() +# @description parse command options and arguments for dbScriptAllDatabasesCommand +dbScriptAllDatabasesCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionJobs="1" + local -i options_parse_optionParsedCountOptionJobs + ((options_parse_optionParsedCountOptionJobs = 0)) || true + optionProgressBar="0" + local -i options_parse_optionParsedCountOptionProgressBar + ((options_parse_optionParsedCountOptionProgressBar = 0)) || true + optionFromDsn="" + local -i options_parse_optionParsedCountOptionFromDsn + ((options_parse_optionParsedCountOptionFromDsn = 0)) || true + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + local -i options_parse_optionParsedCountOptionDatabases + ((options_parse_optionParsedCountOptionDatabases = 0)) || true + optionOutputDir="" + local -i options_parse_optionParsedCountOptionOutputDir + ((options_parse_optionParsedCountOptionOutputDir = 0)) || true + optionLogFormat="none" + local -i options_parse_optionParsedCountOptionLogFormat + ((options_parse_optionParsedCountOptionLogFormat = 0)) || true + + argScriptToExecute="" + local -i options_parse_argParsedCountArgScriptToExecute + ((options_parse_argParsedCountArgScriptToExecute = 0)) || true + + scriptArguments=() + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/20 + # optionJobs alts --jobs|-j + # type: String min 0 max 1 + --jobs | -j) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi - ((argLength = ${#arg})) || true + if ((options_parse_optionParsedCountOptionJobs >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionJobs)) + # shellcheck disable=SC2034 + optionJobs="$1" + optionJobsCallback "${options_parse_arg}" "${optionJobs}" + + ;; + + # Option 2/20 + # optionProgressBar alts --bar|-b + # type: Boolean min 0 max 1 + --bar | -b) + # shellcheck disable=SC2034 + optionProgressBar="1" + + if ((options_parse_optionParsedCountOptionProgressBar >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionProgressBar)) + optionProgressBarCallback "${options_parse_arg}" "${optionProgressBar}" + + ;; + + # Option 3/20 + # optionFromDsn alts --from-dsn|-f + # type: String min 0 max 1 + --from-dsn | -f) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine + if ((options_parse_optionParsedCountOptionFromDsn >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionFromDsn)) + # shellcheck disable=SC2034 + optionFromDsn="$1" + ;; + + # Option 4/20 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 5/20 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 6/20 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 7/20 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 fi - continue - fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" + ;; + + # Option 8/20 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine - fi - ) | sed -E -e 's/[[:blank:]]+$//' -} + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" -# @description list the conf files list available in bash-tools/conf/ folder -# and those overridden in ${HOME}/.bash-tools/ folder -# -# @arg $1 confFolder:String the directory name (not the path) to list -# @arg $2 extension:String the extension (.sh by default) -# @arg $3 indentStr:String the indentation (' - ' by default) can be any string compatible with sed not containing any / -# -# @stdout list of files without extension/directory -# @example text -# - default.local -# - default.remote -# - localhost-root -Conf::getMergedList() { - local confFolder="$1" - local extension="${2-sh}" - local indentStr="${3- - }" + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" - local DEFAULT_CONF_DIR="${FRAMEWORK_ROOT_DIR}/conf/${confFolder}" - local HOME_CONF_DIR="${HOME}/.bash-tools/${confFolder}" + ;; - ( - if [[ -d "${DEFAULT_CONF_DIR}" ]]; then - Conf::list "${DEFAULT_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" - fi - if [[ -d "${HOME_CONF_DIR}" ]]; then - Conf::list "${HOME_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" - fi - ) | sort | uniq -} + # Option 9/20 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" -# @description get absolute conf file from specified conf folder deduced using these rules -# * from absolute file (ignores and ) -# * relative to where script is executed (ignores and ) -# * from home/.bash-tools/ -# * from framework conf/ -# -# @arg $1 confFolder:String the directory name (not the path) to list -# @arg $2 conf:String file to use without extension -# @arg $3 extension:String the extension (.sh by default) -# -# @stdout absolute conf filename -# @exitcode 1 if file is not found in any location -Conf::getAbsoluteFile() { - local confFolder="$1" - local conf="$2" - local extension="${3-.sh}" - if [[ -n "${extension}" && "${extension:0:1}" != "." ]]; then - extension=".${extension}" - fi + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" - testAbs() { - local result - result="$(realpath -e "$1" 2>/dev/null)" - # shellcheck disable=SC2181 - if [[ "$?" = "0" && -f "${result}" ]]; then - echo "${result}" - return 0 - fi - return 1 - } + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" - # conf is absolute file (including extension) - testAbs "${confFolder}${extension}" && return 0 - # conf is absolute file - testAbs "${confFolder}" && return 0 - # conf is absolute file (including extension) - testAbs "${conf}${extension}" && return 0 - # conf is absolute file - testAbs "${conf}" && return 0 + ;; - # relative to where script is executed (including extension) - if [[ -n "${CURRENT_DIR+xxx}" ]]; then - testAbs "$(File::concatenatePath "${CURRENT_DIR}" "${confFolder}")/${conf}${extension}" && return 0 - fi - # from home/.bash-tools/ - testAbs "$(File::concatenatePath "${HOME}/.bash-tools" "${confFolder}")/${conf}${extension}" && return 0 + # Option 10/20 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi - if [[ -n "${FRAMEWORK_ROOT_DIR+xxx}" ]]; then - # from framework conf/ (including extension) - testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}${extension}" && return 0 + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - # from framework conf/ - testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}" && return 0 - fi + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - # file not found - Log::displayError "conf file '${conf}' not found" + ;; - return 1 -} + # Option 11/20 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi -# @description check if command specified exists or return 1 -# with error and message if not -# -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist -# -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 12/20 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" - fi - return 1 - } - return 0 -} + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 13/20 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi -# @description create a new db instance -# Returns immediately if the instance is already initialized -# -# @arg $1 instanceNewInstance:&Map (passed by reference) database instance to use -# @arg $2 dsn:String dsn profile - load the dsn.env profile deduced using rules defined in Conf::getAbsoluteFile -# -# @example -# declare -Agx dbInstance -# Database::newInstance dbInstance "default.local" -# -# @exitcode 1 if dns file not able to loaded -Database::newInstance() { - local -n instanceNewInstance=$1 - local dsn="$2" - local DSN_FILE + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - if [[ -v instanceNewInstance['INITIALIZED'] && "${instanceNewInstance['INITIALIZED']:-0}" == "1" ]]; then - return - fi + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - # final auth file generated from dns file - instanceNewInstance['AUTH_FILE']="" - instanceNewInstance['DSN_FILE']="" + ;; - # check dsn file - DSN_FILE="$(Conf::getAbsoluteFile "dsn" "${dsn}" "env")" || return 1 - Database::checkDsnFile "${DSN_FILE}" || return 1 - instanceNewInstance['DSN_FILE']="${DSN_FILE}" + # Option 14/20 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" - # shellcheck source=/src/Database/testsData/dsn_valid.env - source "${instanceNewInstance['DSN_FILE']}" + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 15/20 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi - instanceNewInstance['USER']="${USER}" - instanceNewInstance['PASSWORD']="${PASSWORD}" - instanceNewInstance['HOSTNAME']="${HOSTNAME}" - instanceNewInstance['PORT']="${PORT}" + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" - # generate authFile for easy authentication - instanceNewInstance['AUTH_FILE']=$(mktemp -p "${TMPDIR:-/tmp}" -t "mysql.XXXXXXXXXXXX") - ( - echo "[client]" - echo "user = ${USER}" - echo "password = ${PASSWORD}" - echo "host = ${HOSTNAME}" - echo "port = ${PORT}" - ) >"${instanceNewInstance['AUTH_FILE']}" + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - # some of those values can be overridden using the dsn file - # SKIP_COLUMN_NAMES enabled by default - instanceNewInstance['SKIP_COLUMN_NAMES']="${SKIP_COLUMN_NAMES:-1}" - instanceNewInstance['SSL_OPTIONS']="${MYSQL_SSL_OPTIONS:---ssl-mode=DISABLED}" - instanceNewInstance['QUERY_OPTIONS']="${MYSQL_QUERY_OPTIONS:---batch --raw --default-character-set=utf8}" - instanceNewInstance['DUMP_OPTIONS']="${MYSQL_DUMP_OPTIONS:---default-character-set=utf8 --compress --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 ${instanceNewInstance['SSL_OPTIONS']}}" - instanceNewInstance['DB_IMPORT_OPTIONS']="${DB_IMPORT_OPTIONS:---connect-timeout=5 --batch --raw --default-character-set=utf8}" + ;; - instanceNewInstance['INITIALIZED']=1 -} + # Option 16/20 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" -# @description set the general options to use on mysql command to query the database -# Differs than setOptions in the way that these options could change each time -# -# @arg $1 instanceSetQueryOptions:&Map (passed by reference) database instance to use -# @arg $2 optionsList:String query options list -Database::setQueryOptions() { - local -n instanceSetQueryOptions=$1 - # shellcheck disable=SC2034 - instanceSetQueryOptions['QUERY_OPTIONS']="$2" -} + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" -# @description databases's list of given mysql server -# -# @example text -# - information_schema -# - mysql -# - performance_schema -# - sys -# -# @arg $1 instanceUserDbList:&Map (passed by reference) database instance to use -# @stdout the list of db except mysql admin ones (see example) -# @exitcode * the query exit code -Database::getUserDbList() { - # shellcheck disable=SC2034 - local -n instanceUserDbList=$1 - # shellcheck disable=SC2016 - local sql='SELECT `schema_name` from INFORMATION_SCHEMA.SCHEMATA WHERE `schema_name` NOT IN("information_schema", "mysql", "performance_schema", "sys")' - Database::query instanceUserDbList "${sql}" -} + ;; -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 + # Option 17/20 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" - fi - fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" - exit 1 - } -} + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" -} + ;; -# @description ensure running user is not root -# @exitcode 1 if current user is root -# @stderr diagnostics information is displayed -Linux::requireExecutedAsUser() { - if [[ "$(id -u)" = "0" ]]; then - Log::fatal "this script should be executed as normal user" - fi -} + # Option 18/20 + # optionDatabases alts --database + # type: StringArray min 0 max 1 + --database) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi -declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR -FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" -LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" -LOG_LAST_LOG_DATE_INIT=1 -LOG_LAST_DURATION_STR="" + if ((options_parse_optionParsedCountOptionDatabases >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDatabases)) + optionDatabases+=("$1") + ;; + + # Option 19/20 + # optionOutputDir alts --output|-o + # type: String min 0 max 1 + --output | -o) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi -# @description compute duration since last call to this function -# the result is set in following env variables. -# in ss.sss (seconds followed by milliseconds precision 3 decimals) -# @noargs -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @set LOG_LAST_LOG_DATE_INIT int (default 1) set to 0 at first call, allows to detect reference log -# @set LOG_LAST_DURATION_STR String the last duration displayed -# @set LOG_LAST_LOG_DATE String the last log date that will be used to compute next diff -Log::computeDuration() { - if ((${DISPLAY_DURATION:-0} == 1)); then - local -i duration=0 - local -i delta=0 - local -i currentLogDate - currentLogDate="${EPOCHREALTIME/[^0-9]/}" - if ((LOG_LAST_LOG_DATE_INIT == 1)); then - LOG_LAST_LOG_DATE_INIT=0 - LOG_LAST_DURATION_STR="Ref" - else - duration=$(((currentLogDate - FIRST_LOG_DATE) / 1000000)) - delta=$(((currentLogDate - LOG_LAST_LOG_DATE) / 1000000)) - LOG_LAST_DURATION_STR="${duration}s/+${delta}s" - fi - LOG_LAST_LOG_DATE="${currentLogDate}" - # shellcheck disable=SC2034 - local microSeconds="${EPOCHREALTIME#*.}" - LOG_LAST_DURATION_STR="$(printf '%(%T)T.%03.0f\n' "${EPOCHSECONDS}" "${microSeconds:0:3}")(${LOG_LAST_DURATION_STR}) - " - else - # shellcheck disable=SC2034 - LOG_LAST_DURATION_STR="" - fi -} + if ((options_parse_optionParsedCountOptionOutputDir >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionOutputDir)) + # shellcheck disable=SC2034 + optionOutputDir="$1" + outputDirectoryCallback "${options_parse_arg}" "${optionOutputDir}" + + ;; + + # Option 20/20 + # optionLogFormat alts --log-format|-l + # type: String min 0 max 1 + # authorizedValues: none|log + --log-format | -l) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ none|log ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([none log])" + return 1 + fi -# @description log message to file -# @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" - fi -} + if ((options_parse_optionParsedCountOptionLogFormat >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFormat)) + # shellcheck disable=SC2034 + optionLogFormat="$1" + outputDirectoryCallback "${options_parse_arg}" "${optionLogFormat}" -# @description log message to file -# @arg $1 message:String the message to display -Log::logDebug() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then - Log::logMessage "${2:-DEBUG}" "$1" - fi -} + ;; -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 1)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((minParsedArgIndex2 = minParsedArgIndex1 + 0)) || true + ((maxParsedArgIndex2 = maxParsedArgIndex1)) || true + ((incrementArg = 1 )) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/2 - argScriptToExecute + # Argument argScriptToExecute min 1 max 1 + # Argument argScriptToExecute authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if ((options_parse_argParsedCountArgScriptToExecute >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument scriptToExecute - Maximum number of argument occurrences reached(1)" + return 1 + fi + ((++options_parse_argParsedCountArgScriptToExecute)) + # shellcheck disable=SC2034 + argScriptToExecute="${options_parse_arg}" + argScriptToExecuteCallback "${argScriptToExecute}" -- "${@:2}" -# @description log message to file -# @arg $1 message:String the message to display -Log::logError() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_ERROR)); then - Log::logMessage "${2:-ERROR}" "$1" - fi -} -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} + # Argument 2/2 - scriptArguments + # Argument scriptArguments min 0 max -1 + # Argument scriptArguments authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 )); then + ((++options_parse_argParsedCountScriptArguments)) + # shellcheck disable=SC2034 + # shellcheck disable=SC2034 + scriptArguments+=("${options_parse_arg}") -# @description log message to file -# @arg $1 message:String the message to display -Log::logFatal() { - Log::logMessage "${2:-FATAL}" "$1" -} -# @description Internal: common log message -# @example text -# [date]|[levelMsg]|message -# -# @example text -# 2020-01-19 19:20:21|ERROR |log error -# 2020-01-19 19:20:21|SKIPPED|log skipped -# -# @arg $1 levelMsg:String message's level description (eg: STATUS, ERROR, ...) -# @arg $2 msg:String the message to display -# @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty -# @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages -# @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad -Log::logMessage() { - local levelMsg="$1" - local msg="$2" - local date + # else too much args + else - if [[ -n "${BASH_FRAMEWORK_LOG_FILE}" ]] && ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - date="$(date '+%Y-%m-%d %H:%M:%S')" - touch "${BASH_FRAMEWORK_LOG_FILE}" - printf "%s|%7s|%s\n" "${date}" "${levelMsg}" "${msg}" >>"${BASH_FRAMEWORK_LOG_FILE}" - fi -} -# @description To be called before logging in the log file -# @arg $1 file:string log file name -# @arg $2 maxLogFilesCount:int maximum number of log files -Log::rotate() { - local file="$1" - local maxLogFilesCount="${2:-5}" + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" + return 1 + fi - if [[ ! -f "${file}" ]]; then - Log::displayDebug "Log file ${file} doesn't exist yet" - return 0 - fi - local i - for ((i = maxLogFilesCount - 1; i > 0; i--)); do - Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" - mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true + fi + if ((incrementArg == 1)); then + ((++options_parse_parsedArgIndex)) + fi + ;; + esac + shift || true done - if cp "${file}" "${file}.1" &>/dev/null; then - echo >"${file}" # reset log file - Log::displayInfo "Log rotation ${file} to ${file}.1" - fi -} -# @description list files of dir with given extension and display it as a list one by line -# -# @arg $1 dir:String the directory to list -# @arg $2 prefix:String the profile file prefix (default: "") -# @arg $3 ext:String the extension -# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') -# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') -# @stdout list of files without extension/directory -# @example text -# - default.local -# - default.remote -# - localhost-root -# @exitcode 1 if directory does not exists -Conf::list() { - local dir="$1" - local prefix="${2:-}" - local ext="${3}" - local findOptions="${4--type f}" - local indentStr="${5- - }" - if [[ ! -d "${dir}" ]]; then - Log::displayError "Directory ${dir} does not exist" - fi - if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then - ext=".${ext}" - fi - ( - # shellcheck disable=SC2086 - cd "${dir}" && - find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | - sed -E "s#^\./${prefix}##g" | - sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" - ) -} -# @description concatenate 2 paths and ensure the path is correct using realpath -m -# @arg $1 basePath:String -# @arg $2 subPath:String -# @require Linux::requireRealpathCommand -File::concatenatePath() { - local basePath="$1" - local subPath="$2" - local fullPath="${basePath:+${basePath}/}${subPath}" - realpath -m "${fullPath}" 2>/dev/null -} -# @description check if dsn file has all the mandatory variables set -# Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT -# -# @arg $1 dsnFileName:String dsn absolute filename -# @set HOSTNAME loaded from dsn file -# @set PORT loaded from dsn file -# @set USER loaded from dsn file -# @set PASSWORD loaded from dsn file -# @exitcode 0 on valid file -# @exitcode 1 if one of the properties of the conf file is invalid or if file not found -# @stderr log output if error found in conf file -Database::checkDsnFile() { - local dsnFileName="$1" - if [[ ! -f "${dsnFileName}" ]]; then - Log::displayError "dsn file ${dsnFileName} not found" - return 1 - fi - ( - unset HOSTNAME PORT PASSWORD USER - # shellcheck source=/src/Database/testsData/dsn_valid.env - source "${dsnFileName}" - if [[ -z ${HOSTNAME+x} ]]; then - Log::displayError "dsn file ${dsnFileName} : HOSTNAME not provided" - return 1 - fi - if [[ -z "${HOSTNAME}" ]]; then - Log::displayWarning "dsn file ${dsnFileName} : HOSTNAME value not provided" - fi - if [[ "${HOSTNAME}" = "localhost" ]]; then - Log::displayWarning "dsn file ${dsnFileName} : check that HOSTNAME should not be 127.0.0.1 instead of localhost" - fi - if [[ -z "${PORT+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : PORT not provided" - return 1 - fi - if ! [[ ${PORT} =~ ^[0-9]+$ ]]; then - Log::displayError "dsn file ${dsnFileName} : PORT invalid" - return 1 - fi - if [[ -z "${USER+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : USER not provided" - return 1 - fi - if [[ -z "${PASSWORD+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : PASSWORD not provided" - return 1 - fi - ) -} -# @description mysql query on a given db -# @warning could use QUERY_OPTIONS variable from dsn if defined -# @example -# cat file.sql | Database::query ... -# @arg $1 instanceQuery:&Map (passed by reference) database instance to use -# @arg $2 sqlQuery:String (optional) sql query or sql file to execute. if not provided or empty, the command can be piped -# @arg $3 dbName:String (optional) the db name -# -# @exitcode mysql command status code -Database::query() { - local -n instanceQuery=$1 - local -a mysqlCommand=() - local -a queryOptions - mysqlCommand+=(mysql) - mysqlCommand+=("--defaults-extra-file=${instanceQuery['AUTH_FILE']}") - IFS=' ' read -r -a queryOptions <<<"${instanceQuery['QUERY_OPTIONS']}" - mysqlCommand+=("${queryOptions[@]}") - if [[ "${instanceQuery['SKIP_COLUMN_NAMES']}" = "1" ]]; then - mysqlCommand+=("-s" "--skip-column-names") - fi - # add optional db name - if [[ -n "${3+x}" ]]; then - mysqlCommand+=("$3") - fi - # add optional sql query - if [[ -n "${2+x}" && -n "$2" && ! -f "$2" ]]; then - mysqlCommand+=("-e") - mysqlCommand+=("$2") - fi - Log::displayDebug "$(printf "execute command: '%s'" "${mysqlCommand[*]}")" - if [[ -f "$2" ]]; then - "${mysqlCommand[@]}" <"$2" - else - "${mysqlCommand[@]}" - fi -} -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} -# @description load color theme -# @noargs -# @env BASH_FRAMEWORK_THEME String theme to use -# @env LOAD_THEME int 0 to avoid loading theme -# @exitcode 0 always successful -UI::requireTheme() { - if [[ "${LOAD_THEME:-1}" = "1" ]]; then - UI::theme "${BASH_FRAMEWORK_THEME-default}" - fi -} -# @description ensure command realpath is available -# @exitcode 1 if realpath command not available -# @stderr diagnostics information is displayed -Linux::requireRealpathCommand() { - Assert::commandExists realpath + + + + + + + + + if ((options_parse_argParsedCountArgScriptToExecute < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'scriptToExecute' should be provided at least 1 time(s)" + return 1 + fi || return $? + + + commandOptionParseFinished + dbScriptAllDatabasesCommandCallback + } -# FUNCTIONS +# @description display command options and arguments help for dbScriptAllDatabasesCommand +dbScriptAllDatabasesCommandHelp() { + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Allows to execute a script on each database of specified mysql server." "" -facade_main_dbScriptAllDatabasessh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + echo + echo -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -Linux::requireRealpathCommand -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir -Linux::requireExecutedAsUser - -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034 + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "dbScriptAllDatabases [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--jobs|-j ]" "[--bar|-b]" "[--from-dsn|-f ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--database ]" "[--output|-o ]" "[--log-format|-l ]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "dbScriptAllDatabases" "${optionsAltList[@]}" + echo -# default values -declare outputDirectory="${HOME}/.bash-tools/output" -declare optionFromDsn="" -declare optionOutputDir="" -declare optionLogFormat="" -declare optionJobs="" -declare argScriptToExecute="" -declare -a scriptArguments=() + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" -# other configuration -declare copyrightBeginYear="2020" -declare QUERIES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbQueries" -declare HOME_QUERIES_DIR="${HOME}/.bash-tools/dbQueries" + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}scriptToExecute${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "The script that will be executed on each databases." + echo + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}scriptArguments${__HELP_NORMAL} {list} (optional)] + " + Array::wrap2 ' ' 76 4 " " "Optional parameters to pass to the script." + echo + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}JOB OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--jobs${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-j ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "The number of databases to query in parallel." + echo -declare -a BASH_FRAMEWORK_ARGV_FILTERED=() -copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" -} + Array::wrap2 ' ' 76 6 " Default value: " "1" + echo -# shellcheck disable=SC2317 # if function is overridden -updateArgListInfoVerboseCallback() { - BASH_FRAMEWORK_ARGV_FILTERED+=(--verbose) -} -# shellcheck disable=SC2317 # if function is overridden -updateArgListDebugVerboseCallback() { - BASH_FRAMEWORK_ARGV_FILTERED+=(-vv) -} -# shellcheck disable=SC2317 # if function is overridden -updateArgListTraceVerboseCallback() { - BASH_FRAMEWORK_ARGV_FILTERED+=(-vvv) -} -# shellcheck disable=SC2317 # if function is overridden -updateArgListEnvFileCallback() { :; } -# shellcheck disable=SC2317 # if function is overridden -updateArgListLogLevelCallback() { :; } -# shellcheck disable=SC2317 # if function is overridden -updateArgListDisplayLevelCallback() { :; } -# shellcheck disable=SC2317 # if function is overridden -updateArgListNoColorCallback() { - BASH_FRAMEWORK_ARGV_FILTERED+=(--no-color) -} -# shellcheck disable=SC2317 # if function is overridden -updateArgListThemeCallback() { :; } -# shellcheck disable=SC2317 # if function is overridden -updateArgListQuietCallback() { :; } + echo -e " ${__HELP_OPTION_COLOR}--bar${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-b${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Show progress as a progress bar. In the bar is shown: % of jobs completed," "estimated seconds left, and number of jobs started." "" + echo -# shellcheck disable=SC2317 # if function is overridden -optionHelpCallback() { - dbScriptAllDatabasesCommand help - exit 0 -} -# shellcheck disable=SC2317 # if function is overridden -optionVersionCallback() { - echo "${SCRIPT_NAME} version 2.0" - exit 0 -} + echo + echo -e "${__HELP_TITLE_COLOR}SOURCE OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Target mysql server." + echo -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} -# shellcheck disable=SC2317 # if function is overridden -optionInfoVerboseCallback() { - BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' - BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" -} + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo -# shellcheck disable=SC2317 # if function is overridden -optionDebugVerboseCallback() { - BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' - BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" -} -# shellcheck disable=SC2317 # if function is overridden -optionTraceVerboseCallback() { - BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' - BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" -} -getLevel() { - local levelName="$1" - case "${levelName^^}" in - OFF) - echo "${__LEVEL_OFF}" - ;; - ERR | ERROR) - echo "${__LEVEL_ERROR}" - ;; - WARN | WARNING) - echo "${__LEVEL_WARNING}" - ;; - INFO) - echo "${__LEVEL_INFO}" - ;; - DEBUG | TRACE) - echo "${__LEVEL_DEBUG}" - ;; - *) - Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" - return 1 - esac -} + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo -getVerboseLevel() { - local levelName="$1" - case "${levelName^^}" in - OFF) - echo "${__VERBOSE_LEVEL_OFF}" - ;; - ERR | ERROR | WARN | WARNING | INFO) - echo "${__VERBOSE_LEVEL_INFO}" - ;; - DEBUG) - echo "${__VERBOSE_LEVEL_DEBUG}" - ;; - TRACE) - echo "${__VERBOSE_LEVEL_TRACE}" - ;; - *) - Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" - return 1 - esac -} -# shellcheck disable=SC2317 # if function is overridden -optionDisplayLevelCallback() { - local level="$2" - local logLevel verboseLevel - logLevel="$(getLevel "${level}")" - verboseLevel="$(getVerboseLevel "${level}")" - BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" -} -# shellcheck disable=SC2317 # if function is overridden -optionLogLevelCallback() { - local level="$2" - local logLevel verboseLevel - logLevel="$(getLevel "${level}")" - verboseLevel="$(getVerboseLevel "${level}")" - BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" -} + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo -# shellcheck disable=SC2317 # if function is overridden -optionLogFileCallback() { - local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" -} -# shellcheck disable=SC2317 # if function is overridden -optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" -} -# shellcheck disable=SC2317 # if function is overridden -optionNoColorCallback() { - UI::theme "noColor" -} + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo -# shellcheck disable=SC2317 # if function is overridden -optionThemeCallback() { - UI::theme "$2" -} -displayConfig() { - echo "Config" - UI::drawLine "-" - local var - while read -r var; do - printf '%-40s = %s\n' "${var}" "$(declare -p "${var}" | sed -E -e 's/^[^=]+=(.*)/\1/')" - done < <(typeset -p | awk 'match($3, "^(BASH_FRAMEWORK_[^=]+)=", m) { print m[1] }' | sort) - exit 0 -} -optionBashFrameworkConfigCallback() { - if [[ ! -f "$2" ]]; then - Log::fatal "Command ${SCRIPT_NAME} - Bash framework config file '$2' does not exists" - fi -} + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + -defaultFrameworkConfig="$( - cat <<'EOF' -# copied from src/_includes/.framework-config.default -# shellcheck disable=SC2034 + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo -REAL_SCRIPT_FILE="${REAL_SCRIPT_FILE:-$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")}" -FRAMEWORK_ROOT_DIR="${FRAMEWORK_ROOT_DIR:-${REAL_SCRIPT_FILE%/*/*}}" -FRAMEWORK_SRC_DIR="${FRAMEWORK_SRC_DIR:-${FRAMEWORK_ROOT_DIR}/src}" -FRAMEWORK_BIN_DIR="${FRAMEWORK_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/bin}" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_VENDOR_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/vendor/bin}" -# describe the functions that will be skipped from being imported -FRAMEWORK_FUNCTIONS_IGNORE_REGEXP="${FRAMEWORK_FUNCTIONS_IGNORE_REGEXP:-^(Namespace::functions|Functions::myFunction|Namespace::requireSomething|Acquire::ForceIPv4)$}" -# describe the files that do not contain function to be imported -NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/_binaries|^src/_includes|^src/batsHeaders.sh$|^src/_standalone)}" -# describe the files that are allowed to not have an associated bats file -BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" -# describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" -# Source directories -if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then - FRAMEWORK_SRC_DIRS=( - "${FRAMEWORK_ROOT_DIR}/src" - ) -fi + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo -# export here all the variables that will be used in your templates -export REPOSITORY_URL="${REPOSITORY_URL:-https://github.com/fchastanet/bash-tools-framework}" -BASH_FRAMEWORK_THEME="${BASH_FRAMEWORK_THEME:-default}" -BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" -BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" -BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" -BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" -EOF -)" -overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo -commandOptionParseFinished() { - # load default template framework config - defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" - local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi - # shellcheck disable=SC2154 - if [[ -f "${optionBashFrameworkConfig}" ]]; then - files+=("${optionBashFrameworkConfig}") - fi - files+=("${overrideEnvFile}") - Env::requireLoad "${files[@]}" - Log::requireLoad - # shellcheck disable=SC2154 - if [[ "${optionConfig}" = "1" ]]; then - displayConfig - fi -} + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo -# default values -declare optionFromDsn="" + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo -optionJobsCallback() { - if ! [[ ${optionJobs} =~ ^[0-9]+$ ]]; then - Log::fatal "number of jobs is incorrect" - fi - if [[ ${optionJobs} -lt 1 ]]; then - Log::fatal "number of jobs must be greater than 0" - fi -} -declare -a PARALLEL_OPTIONS + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo -optionProgressBarCallback() { - PARALLEL_OPTIONS+=(--bar) -} -dbScriptAllDatabasesCommand() { - local options_parse_cmd="$1" - shift || true + echo + echo -e "${__HELP_TITLE_COLOR}SCRIPTS OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--database ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "If provided will check only this db, otherwise script will be executed on all dbs of mysql server." + echo - if [[ "${options_parse_cmd}" = "parse" ]]; then - optionJobs="1" - local -i options_parse_optionParsedCountOptionJobs - ((options_parse_optionParsedCountOptionJobs = 0)) || true - optionProgressBar="0" - local -i options_parse_optionParsedCountOptionProgressBar - ((options_parse_optionParsedCountOptionProgressBar = 0)) || true - local -i options_parse_optionParsedCountOptionFromDsn - ((options_parse_optionParsedCountOptionFromDsn = 0)) || true - local -i options_parse_optionParsedCountOptionOutputDir - ((options_parse_optionParsedCountOptionOutputDir = 0)) || true - optionLogFormat="none" - local -i options_parse_optionParsedCountOptionLogFormat - ((options_parse_optionParsedCountOptionLogFormat = 0)) || true - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountArgScriptToExecute - ((options_parse_argParsedCountArgScriptToExecute = 0)) || true - local -i options_parse_argParsedCountScriptArguments - ((options_parse_argParsedCountScriptArguments = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/20 - # Option optionJobs --jobs|-j variableType String min 0 max 1 authorizedValues '' regexp '' - --jobs | -j) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionJobs >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionJobs)) - # shellcheck disable=SC2034 - optionJobs="$1" - optionJobsCallback "${options_parse_arg}" "${optionJobs}" - ;; - # Option 2/20 - # Option optionProgressBar --bar|-b variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --bar | -b) - # shellcheck disable=SC2034 - optionProgressBar="1" - if ((options_parse_optionParsedCountOptionProgressBar >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionProgressBar)) - optionProgressBarCallback "${options_parse_arg}" - ;; - # Option 3/20 - # Option optionFromDsn --from-dsn|-f variableType String min 0 max 1 authorizedValues '' regexp '' - --from-dsn | -f) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionFromDsn >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionFromDsn)) - # shellcheck disable=SC2034 - optionFromDsn="$1" - ;; - # Option 4/20 - # Option optionDatabases --database variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --database) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionDatabases)) - optionDatabases+=("$1") - ;; - # Option 5/20 - # Option optionOutputDir --output|-o variableType String min 0 max 1 authorizedValues '' regexp '' - --output | -o) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionOutputDir >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionOutputDir)) - # shellcheck disable=SC2034 - optionOutputDir="$1" - outputDirectoryCallback "${options_parse_arg}" "${optionOutputDir}" - ;; - # Option 6/20 - # Option optionLogFormat --log-format|-l variableType String min 0 max 1 authorizedValues 'none|log' regexp '' - --log-format | -l) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ none|log ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(none|log)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFormat >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFormat)) - # shellcheck disable=SC2034 - optionLogFormat="$1" - ;; - # Option 7/20 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 8/20 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 9/20 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 10/20 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 11/20 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 12/20 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 13/20 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 14/20 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 15/20 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 16/20 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 17/20 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 18/20 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 19/20 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) - # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 20/20 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) - # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" - return 1 - fi - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/2 - # Argument argScriptToExecute min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 0 && options_parse_parsedArgIndex < 1)); then - if ((options_parse_argParsedCountArgScriptToExecute >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument scriptToExecute - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountArgScriptToExecute)) - # shellcheck disable=SC2034 - argScriptToExecute="${options_parse_arg}" - argScriptToExecuteCallback "${argScriptToExecute}" -- "${@:2}" - # Argument 2/2 - # Argument scriptArguments min 0 max -1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 1)); then - ((++options_parse_argParsedCountScriptArguments)) - # shellcheck disable=SC2034 - scriptArguments+=("${options_parse_arg}") - else - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" - return 1 - fi - fi - ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - if ((options_parse_argParsedCountArgScriptToExecute < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'scriptToExecute' should be provided at least 1 time(s)" - return 1 - fi - commandOptionParseFinished - dbScriptAllDatabasesCommandCallback - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "Allows to execute a script on each database of specified mysql server" - echo - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--jobs|-j ]" "[--bar|-b]" "[--from-dsn|-f ]" "[--database ]" "[--output|-o ]" "[--log-format|-l ]" "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}scriptToExecute${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(the\ script\ that\ will\ be\ executed\ on\ each\ databases) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " [${__HELP_OPTION_COLOR}scriptArguments${__HELP_NORMAL} {list} (optional)]" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(optional\ parameters\ to\ pass\ to\ the\ script) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--jobs${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-j ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(specify\ the\ number\ of\ db\ to\ query\ in\ parallel) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: 1' - echo -e " ${__HELP_OPTION_COLOR}--bar${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-b${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Show\ progress\ as\ a\ progress\ bar.\ In\ the\ bar\ is\ shown:\ %\ of\ jobs\ completed\,\ estimated\ seconds\ left\,\ and\ number\ of\ jobs\ started.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}SCRIPT OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(target\ mysql\ server) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--database ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(if\ provided\ will\ check\ only\ this\ db\,\ otherwise\ script\ will\ be\ executed\ on\ all\ dbs\ of\ mysql\ server) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--output${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-o ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(output\ directory\,\ see\ log-format\ option) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-format${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-l ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(if\ output\ dir\ provided\,\ will\ log\ each\ db\ result\ to\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: none' - echo ' Possible values: none|log' - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e """ -${__HELP_TITLE}NOTE:${__HELP_NORMAL} -the use of output, log-format, verbose options highly depends on the script used - -${__HELP_TITLE}LIST OF AVAILABLE DSN:${__HELP_NORMAL} -${dsnList} - -${__HELP_TITLE}DEFAULT QUERIES DIRECTORY:${__HELP_NORMAL} -${QUERIES_DIR-configuration error} - -${__HELP_TITLE}USER QUERIES DIRECTORY:${__HELP_NORMAL} -${HOME_QUERIES_DIR-configuration error} -Allows to override queries defined in "Default queries directory" - -${__HELP_TITLE}LIST OF AVAILABLE SCRIPTS:${__HELP_NORMAL} -${scriptsList} - -${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL} script conf/dbScripts/extractData.sh - executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using - ${__HELP_EXAMPLE}$0 -j 10 extractData databaseSize${__HELP_NORMAL} - - executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages) - ${__HELP_EXAMPLE}$0 -j 10 --log-format none extractData databaseSize${__HELP_NORMAL} - - use --verbose to get some debug information - ${__HELP_EXAMPLE}$0 -j 10 --log-format none --verbose extractData databaseSize${__HELP_NORMAL} - -${__HELP_TITLE}USE CASES:${__HELP_NORMAL} - you can use this script in order to check that each db model conforms with your ORM schema - simply create a new script in conf/dbQueries that will call your orm schema checker - - update multiple db at once (simple to complex update script)""" - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '2.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" - return 1 - fi -} -optionHelpCallback() { - local dsnList queriesList scriptsList - dsnList="$(Conf::getMergedList "dsn" "env")" - queriesList="$(Conf::getMergedList "dbQueries" "sql" || true)" - scriptsList="$(Conf::getMergedList "dbScripts" "sh")" + echo -e " ${__HELP_OPTION_COLOR}--output${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-o ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Output directory, see log-format option." + echo - dbScriptAllDatabasesCommand help | envsubst - exit 0 -} -outputDirectoryCallback() { - if [[ "${optionOutputDir:0:1}" != "/" ]]; then - # relative path - optionOutputDir="${PWD}/${optionOutputDir}" - fi - mkdir -p "${optionOutputDir}" || Log::fatal "unable to create directory ${optionOutputDir}" - if [[ ! -d "${optionOutputDir}" || ! -w "${optionOutputDir}" ]]; then - Log::fatal "output dir is not correct or not writable" - fi -} -argScriptToExecuteCallback() { - if [[ ! -f "${argScriptToExecute}" ]]; then - declare scriptAbsoluteFile - scriptAbsoluteFile="$(Conf::getAbsoluteFile "dbScripts" "${argScriptToExecute}" "sh")" && { - argScriptToExecute="${scriptAbsoluteFile}" - if (( BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG )); then - Log::displayInfo "Using script file ${scriptAbsoluteFile}" - fi - } - fi -} + echo -e " ${__HELP_OPTION_COLOR}--log-format${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-l ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "If output dir provided, will log each db result to log file." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "none, " "log" + echo -dbScriptAllDatabasesCommandCallback() { - if [[ -z "${optionFromDsn}" ]]; then - # default value for FROM_DSN if from-aws not set - optionFromDsn="default.remote" - fi + Array::wrap2 ' ' 76 6 " Default value: " "none" + echo + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "2.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" } -dbScriptAllDatabasesCommand parse "${BASH_FRAMEWORK_ARGV[@]}" -# @require Linux::requireExecutedAsUser -run() { +beforeParseCallback +initConf - # check dependencies - Assert::commandExists mysql "sudo apt-get install -y mysql-client" - Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" - Assert::commandExists parallel "sudo apt-get install -y parallel" +dbScriptAllDatabasesCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { - # create db instance - declare -Agx dbInstance - Database::newInstance dbInstance "${optionFromDsn}" - Database::setQueryOptions dbInstance "${dbInstance['QUERY_OPTIONS']} --connect-timeout=5" - if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then - Log::displayInfo "Using dsn ${dbInstance['DSN_FILE']}" - fi +# create db instance +declare -Agx dbInstance +# shellcheck disable=SC2154 +Database::newInstance dbInstance "${optionFromDsn}" +Database::setQueryOptions dbInstance "${dbInstance['QUERY_OPTIONS']} --connect-timeout=5" +if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + Log::displayInfo "Using dsn ${dbInstance['DSN_FILE']}" +fi - # list of all databases - if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then - Log::displayInfo "get the list of all databases" - fi +# list of all databases +if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + Log::displayInfo "get the list of all databases" +fi - if ((${#optionDatabases[@]} == 0)); then - mapfile -t optionDatabases < <(Database::getUserDbList dbInstance) - fi +if ((${#optionDatabases[@]} == 0)); then + mapfile -t optionDatabases < <(Database::getUserDbList dbInstance) +fi - if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then - Log::displayInfo "processing ${#optionDatabases[@]} databases using ${optionJobs} jobs" - fi +if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + # shellcheck disable=SC2154 + Log::displayInfo "processing ${#optionDatabases[@]} databases using ${optionJobs} jobs" +fi - export selectedQueryFile - export MYSQL_OPTIONS +export selectedQueryFile +export MYSQL_OPTIONS +PARALLEL_OPTIONS+=("-j" "${optionJobs}") - printf '%s\n' "${optionDatabases[@]}" | parallel --eta --progress --tag --jobs="${optionJobs}" \ +# shellcheck disable=SC2154 +printf '%s\n' "${optionDatabases[@]}" | + SHELL=$(type -p bash) \ + parallel --bar --eta --progress --tag "${PARALLEL_OPTIONS[@]}" \ "${argScriptToExecute}" "${optionFromDsn}" "${optionLogFormat}" "${BASH_FRAMEWORK_ARGS_VERBOSE}" \ "${optionOutputDir}" "${PWD}" "${scriptArguments[@]}" -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi } -facade_main_dbScriptAllDatabasessh "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml b/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml index bbab2601..c7c18a62 100644 --- a/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml +++ b/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml @@ -1,5 +1,6 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsFromDsn.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -38,16 +39,6 @@ binData: - --profile - -p - - variableName: optionFromDsn - group: OptionsGroup - type: String - defaultValue: default.remote - help: optionFromDsnHelpFunction - helpValueName: dsn - alts: - - --from-dsn - - -f - - variableName: optionRatio group: OptionsGroup type: String diff --git a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt index 4c19ec9c..5246d038 100644 --- a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt +++ b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt @@ -2,17 +2,20 @@ Generate optimized profiles to be used by dbImport. USAGE: dbImportProfile [OPTIONS] [ARGUMENTS] -USAGE: dbImportProfile [--help|-h] [--config] +USAGE: dbImportProfile [--from-dsn|-f ] [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] [--env-file ] [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] - [--quiet|-q] [--profile|-p ] [--from-dsn|-f ] - [--ratio|-r ] + [--quiet|-q] [--profile|-p ] [--ratio|-r ] ARGUMENTS: fromDbName {single} (mandatory) The name of the source/remote database. +SOURCE OPTIONS: + --from-dsn, -f  {single} + Target mysql server. + GLOBAL OPTIONS: --help, -h {single} Displays this command help @@ -54,11 +57,6 @@ The name of the profile to write in profiles directory. If not provided, the file name pattern will be 'auto__.sh' - --from-dsn, -f  {single} - dsn to use for source database (Default: default.remote) - - if not provided, the file name pattern will be 'auto__.sh' - Default value: default.remote --ratio, -r  {single} define the ratio to use (0 to 100% - default 70). diff --git a/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml b/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml index 590147d9..12d3701e 100644 --- a/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml +++ b/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml @@ -1,6 +1,6 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsJobs.yaml" - - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsProgressBar.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsFromDsn.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -30,13 +30,13 @@ binData: 11: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh 99: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-main.sh - optionGroups: - groupSourceDbOptions: - title: "QUERY OPTIONS:" help: "Execute a query on multiple databases to generate a tsv format report.\\n The query can be parallelized on multiple databases." longDescription: longDescriptionFunction + optionGroups: + groupQueryOptions: + title: "QUERY OPTIONS:" args: - type: String min: 1 @@ -48,7 +48,7 @@ binData: - argQueryCallback options: - variableName: optionSeparator - group: groupSourceDbOptions + group: groupQueryOptions type: String help: Character to use to separate mysql column. helpValueName: separator @@ -58,11 +58,3 @@ binData: alts: - --separator - -s - - variableName: optionFromDsn - group: groupSourceDbOptions - type: String - help: Target mysql server. - helpValueName: dsn - alts: - - --from-dsn - - -f diff --git a/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt index 2bf72ab4..f76e3e14 100644 --- a/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt +++ b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt @@ -1,18 +1,20 @@ SYNOPSIS: Execute a query on multiple databases to generate a tsv format report. + The query can be parallelized on multiple databases. USAGE: dbQueryAllDatabases [OPTIONS] [ARGUMENTS] USAGE: dbQueryAllDatabases [--jobs|-j ] [--bar|-b] - [--help|-h] [--config] [--bash-framework-config ] - [--verbose|-v] [-vv] [-vvv] [--env-file ] [--log-level ] - [--log-file ] [--display-level ] [--no-color] - [--theme ] [--version] [--quiet|-q] [--separator|-s ] - [--from-dsn|-f ] + [--from-dsn|-f ] [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] [--separator|-s ] ARGUMENTS: argQuery {single} (mandatory) - Query to execute + Query to execute + - , try to execute the mysql query provided by the file - , search for query file in queries directory (see below) @@ -22,14 +24,16 @@ --jobs, -j  {single} The number of databases to query in parallel. Default value: 1 - -PROGRESS BAR OPTIONS: --bar, -b {single} Show progress as a progress bar. In the bar is shown: % of jobs completed, estimated seconds left, and number of jobs started. +SOURCE OPTIONS: + --from-dsn, -f  {single} + Target mysql server. + GLOBAL OPTIONS: --help, -h {single} Displays this command help @@ -70,8 +74,6 @@ --separator, -s  {single} Character to use to separate mysql column. Default value: | - --from-dsn, -f  {single} - Target mysql server. DESCRIPTION: diff --git a/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml b/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml new file mode 100644 index 00000000..b6f6ceb4 --- /dev/null +++ b/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml @@ -0,0 +1,89 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsJobs.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsFromDsn.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbScriptAllDatabases" + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: dbScriptAllDatabasesCommand + version: "2.0" + commandName: dbScriptAllDatabases + beforeParseCallbacks: + - beforeParseCallback + - initConf + callbacks: + - dbScriptAllDatabasesCommandCallback@100 + definitionFiles: + 11: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-main.sh + help: | + Allows to execute a script on each database of specified mysql server. + longDescription: longDescriptionFunction + optionGroups: + groupScriptsOptions: + title: "SCRIPTS OPTIONS:" + args: + - help: The script that will be executed on each databases. + type: String + min: 1 + max: 1 + name: scriptToExecute + callbacks: + - argScriptToExecuteCallback + variableName: argScriptToExecute + + - help: Optional parameters to pass to the script. + type: StringArray + min: 0 + max: -1 + name: scriptArguments + variableName: scriptArguments + + options: + - variableName: optionDatabases + group: groupScriptsOptions + type: StringArray + help: + If provided will check only this db, otherwise script will be + executed on all dbs of mysql server. + helpValueName: dbName + alts: + - --database + + - variableName: optionOutputDir + group: groupScriptsOptions + type: String + help: Output directory, see log-format option. + helpValueName: outputDirectory + callbacks: + - outputDirectoryCallback + alts: + - --output + - -o + + - variableName: optionLogFormat + group: groupScriptsOptions + type: String + help: If output dir provided, will log each db result to log file. + helpValueName: logFormat + authorizedValuesList: + - none + - log + defaultValue: none + callbacks: + - outputDirectoryCallback + alts: + - --log-format + - -l diff --git a/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-main.sh b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-main.sh new file mode 100755 index 00000000..9964305b --- /dev/null +++ b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-main.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# create db instance +declare -Agx dbInstance +# shellcheck disable=SC2154 +Database::newInstance dbInstance "${optionFromDsn}" +Database::setQueryOptions dbInstance "${dbInstance['QUERY_OPTIONS']} --connect-timeout=5" +if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + Log::displayInfo "Using dsn ${dbInstance['DSN_FILE']}" +fi + +# list of all databases +if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + Log::displayInfo "get the list of all databases" +fi + +if ((${#optionDatabases[@]} == 0)); then + mapfile -t optionDatabases < <(Database::getUserDbList dbInstance) +fi + +if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + # shellcheck disable=SC2154 + Log::displayInfo "processing ${#optionDatabases[@]} databases using ${optionJobs} jobs" +fi + +export selectedQueryFile +export MYSQL_OPTIONS +PARALLEL_OPTIONS+=("-j" "${optionJobs}") + +# shellcheck disable=SC2154 +printf '%s\n' "${optionDatabases[@]}" | + SHELL=$(type -p bash) \ + parallel --bar --eta --progress --tag "${PARALLEL_OPTIONS[@]}" \ + "${argScriptToExecute}" "${optionFromDsn}" "${optionLogFormat}" "${BASH_FRAMEWORK_ARGS_VERBOSE}" \ + "${optionOutputDir}" "${PWD}" "${scriptArguments[@]}" diff --git a/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh new file mode 100755 index 00000000..596994cb --- /dev/null +++ b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash + +declare SCRIPTS_DIR +declare HOME_SCRIPTS_DIR +# shellcheck disable=SC2034 +declare versionNumber="2.0" +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare defaultFromDsn="default.remote" +# shellcheck disable=SC2034 +declare outputDirectory="${HOME}/.bash-tools/output" + +beforeParseCallback() { + Assert::commandExists mysql "sudo apt-get install -y mysql-client" + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" + Assert::commandExists parallel "sudo apt-get install -y parallel" + + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad + Linux::requireExecutedAsUser + Linux::requireRealpathCommand +} + +initConf() { + # shellcheck disable=SC2034 + SCRIPTS_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbScripts" + HOME_SCRIPTS_DIR="${HOME}/.bash-tools/dbScripts" + Db::checkRequirements +} + +optionHelpCallback() { + dbScriptAllDatabasesCommandHelp + exit 0 +} + +longDescriptionFunction() { + local dsnList scriptsList + dsnList="$(Conf::getMergedList "dsn" "env")" + scriptsList="$(Conf::getMergedList "dbScripts" "sh")" + + echo -e "${__HELP_TITLE}NOTE:${__HELP_NORMAL}" + echo -e "the use of output, log-format, verbose options highly depends on the script used" + echo + echo -e "${__HELP_TITLE}LIST OF AVAILABLE DSN:${__HELP_NORMAL}" + echo -e "${dsnList}" + echo + echo -e "${__HELP_TITLE}DEFAULT SCRIPTS DIRECTORY:${__HELP_NORMAL}" + echo -e "${SCRIPTS_DIR-configuration error}" + echo + echo -e "${__HELP_TITLE}USER SCRIPTS DIRECTORY:${__HELP_NORMAL}" + echo -e "${HOME_SCRIPTS_DIR-configuration error}" + echo -e "Allows to override queries defined in 'Default scripts directory'" + echo + echo -e "${__HELP_TITLE}LIST OF AVAILABLE SCRIPTS:${__HELP_NORMAL}" + echo -e "${scriptsList}" + echo + echo -e "${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL} script conf/dbScripts/extractData.sh" + echo -e " executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using" + echo -e " ${__HELP_EXAMPLE}$0 -j 10 extractData databaseSize${__HELP_NORMAL}" + echo + echo -e " executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages)" + echo -e " ${__HELP_EXAMPLE}$0 -j 10 --log-format none extractData databaseSize${__HELP_NORMAL}" + echo + echo -e " use --verbose to get some debug information" + echo -e " ${__HELP_EXAMPLE}$0 -j 10 --log-format none --verbose extractData databaseSize${__HELP_NORMAL}" + echo + echo -e "${__HELP_TITLE}USE CASES:${__HELP_NORMAL}" + echo -e " you can use this script in order to check that each db model conforms with your ORM schema" + echo -e " simply create a new script in conf/dbQueries that will call your orm schema checker" + echo + echo -e " update multiple db at once (simple to complex update script)" +} + +outputDirectoryCallback() { + if [[ "${optionOutputDir:0:1}" != "/" ]]; then + # relative path + optionOutputDir="${PWD}/${optionOutputDir}" + fi + mkdir -p "${optionOutputDir}" || Log::fatal "unable to create directory ${optionOutputDir}" + if [[ ! -d "${optionOutputDir}" || ! -w "${optionOutputDir}" ]]; then + Log::fatal "output dir is not correct or not writable" + fi +} + +argScriptToExecuteCallback() { + if [[ ! -f "${argScriptToExecute}" ]]; then + declare scriptAbsoluteFile + scriptAbsoluteFile="$(Conf::getAbsoluteFile "dbScripts" "${argScriptToExecute}" "sh")" && { + argScriptToExecute="${scriptAbsoluteFile}" + if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + Log::displayInfo "Using script file ${scriptAbsoluteFile}" + fi + } + fi +} + +dbScriptAllDatabasesCommandCallback() { + if [[ -z "${optionFromDsn}" ]]; then + # default value for FROM_DSN if from-aws not set + optionFromDsn="${defaultFromDsn}" + fi +} diff --git a/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.bats b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases.bats similarity index 93% rename from src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.bats rename to src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases.bats index 38419764..a032b493 100755 --- a/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.bats +++ b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck source=src/batsHeaders.sh -source "$(cd "${BATS_TEST_DIRNAME}/../.." && pwd)/batsHeaders.sh" +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd)/batsHeaders.sh" setup() { export TMPDIR="${BATS_TEST_TMPDIR}" @@ -59,7 +59,7 @@ function Database::dbScriptAllDatabases::extractData { #@test # shellcheck disable=SC2016 stub parallel \ - '--eta --progress --tag --jobs=1 * * * * * * * * : while IFS= read -r db; do "${@:5}" "${db}"; done' + '--bar --eta --progress --tag -j 1 * * * * * * * * : while IFS= read -r db; do "${@:7}" "${db}"; done' run "${binDir}/dbScriptAllDatabases" \ -f "${BATS_TEST_DIRNAME}/testsData/databaseSize.envProvided.sh" \ diff --git a/src/_binaries/DbScriptAllDatabases/testsData/databaseSize.envProvided.sh b/src/_binaries/Database/dbScriptAllDatabases/testsData/databaseSize.envProvided.sh similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/databaseSize.envProvided.sh rename to src/_binaries/Database/dbScriptAllDatabases/testsData/databaseSize.envProvided.sh diff --git a/src/_binaries/DbScriptAllDatabases/testsData/databaseSize.result_db1 b/src/_binaries/Database/dbScriptAllDatabases/testsData/databaseSize.result_db1 similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/databaseSize.result_db1 rename to src/_binaries/Database/dbScriptAllDatabases/testsData/databaseSize.result_db1 diff --git a/src/_binaries/DbScriptAllDatabases/testsData/databaseSize.result_db2 b/src/_binaries/Database/dbScriptAllDatabases/testsData/databaseSize.result_db2 similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/databaseSize.result_db2 rename to src/_binaries/Database/dbScriptAllDatabases/testsData/databaseSize.result_db2 diff --git a/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt new file mode 100644 index 00000000..b6f9311a --- /dev/null +++ b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt @@ -0,0 +1,122 @@ +SYNOPSIS: + Allows to execute a script on each database of specified mysql server. + +USAGE: dbScriptAllDatabases [OPTIONS] [ARGUMENTS] +USAGE: dbScriptAllDatabases [--jobs|-j ] [--bar|-b] + [--from-dsn|-f ] [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] [--database ] [--output|-o ] + [--log-format|-l ] + +ARGUMENTS: + scriptToExecute {single} (mandatory) + The script that will be executed on each databases. + [scriptArguments {list} (optional)] + Optional parameters to pass to the script. + +JOB OPTIONS: + --jobs, -j  {single} + The number of databases to query in parallel. + Default value: 1 + --bar, -b {single} + + Show progress as a progress bar. In the bar is shown: % of jobs completed, + estimated seconds left, and number of jobs started. + + +SOURCE OPTIONS: + --from-dsn, -f  {single} + Target mysql server. + +GLOBAL OPTIONS: + --help, -h {single} + Displays this command help + --config {single} + Displays configuration + --bash-framework-config  {single} + Use alternate bash framework configuration. + --verbose, -v {single} + Info level verbose mode (alias of --display-level INFO) + -vv {single} + Debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + Trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level  {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file  {single} + Set log file + --display-level  {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor + Default value: default + --version {single} + Print version information and quit. + --quiet, -q {single} + Quiet mode, doesn't display any output. + +SCRIPTS OPTIONS: + --database  {single} + If provided will check only this db, otherwise script will be executed o + n all dbs of mysql server. + --output, -o  {single} + Output directory, see log-format option. + --log-format, -l  {single} + If output dir provided, will log each db result to log file. + Possible values: none, log + Default value: none + + +DESCRIPTION: +NOTE: +the use of output, log-format, verbose options highly depends on the script used + +LIST OF AVAILABLE DSN: + - dsn_local + - dsn_valid + +DEFAULT SCRIPTS DIRECTORY: +/bash/conf/dbScripts + +USER SCRIPTS DIRECTORY: +home/.bash-tools/dbScripts +Allows to override queries defined in 'Default scripts directory' + +LIST OF AVAILABLE SCRIPTS: + + +EXAMPLES: script conf/dbScripts/extractData.sh + executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using + /bash/bin/dbScriptAllDatabases -j 10 extractData databaseSize + + executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages) + /bash/bin/dbScriptAllDatabases -j 10 --log-format none extractData databaseSize + + use --verbose to get some debug information + /bash/bin/dbScriptAllDatabases -j 10 --log-format none --verbose extractData databaseSize + +USE CASES: + you can use this script in order to check that each db model conforms with your ORM schema + simply create a new script in conf/dbQueries that will call your orm schema checker + + update multiple db at once (simple to complex update script) + +VERSION: 2.0 + +AUTHOR: [François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml + +LICENSE: MIT License +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/DbScriptAllDatabases/testsData/dbScriptAllDatabases.result b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.result similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/dbScriptAllDatabases.result rename to src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.result diff --git a/src/_binaries/DbScriptAllDatabases/testsData/dsn_local.env b/src/_binaries/Database/dbScriptAllDatabases/testsData/dsn_local.env similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/dsn_local.env rename to src/_binaries/Database/dbScriptAllDatabases/testsData/dsn_local.env diff --git a/src/_binaries/DbScriptAllDatabases/testsData/dsn_valid.env b/src/_binaries/Database/dbScriptAllDatabases/testsData/dsn_valid.env similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/dsn_valid.env rename to src/_binaries/Database/dbScriptAllDatabases/testsData/dsn_valid.env diff --git a/src/_binaries/DbScriptAllDatabases/testsData/getUserDbList.result b/src/_binaries/Database/dbScriptAllDatabases/testsData/getUserDbList.result similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/getUserDbList.result rename to src/_binaries/Database/dbScriptAllDatabases/testsData/getUserDbList.result diff --git a/src/_binaries/DbScriptAllDatabases/testsData/parallel b/src/_binaries/Database/dbScriptAllDatabases/testsData/parallel similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/parallel rename to src/_binaries/Database/dbScriptAllDatabases/testsData/parallel diff --git a/src/_binaries/DbScriptAllDatabases/testsData/parallelDbScriptAllDatabases b/src/_binaries/Database/dbScriptAllDatabases/testsData/parallelDbScriptAllDatabases similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/parallelDbScriptAllDatabases rename to src/_binaries/Database/dbScriptAllDatabases/testsData/parallelDbScriptAllDatabases diff --git a/src/_binaries/DbScriptAllDatabases/testsData/pv b/src/_binaries/Database/dbScriptAllDatabases/testsData/pv similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/pv rename to src/_binaries/Database/dbScriptAllDatabases/testsData/pv diff --git a/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.options.tpl b/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.options.tpl deleted file mode 100644 index ab637986..00000000 --- a/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.options.tpl +++ /dev/null @@ -1,159 +0,0 @@ -% -declare versionNumber="2.0" -declare commandFunctionName="dbScriptAllDatabasesCommand" -declare help="Allows to execute a script on each database of specified mysql server" -# shellcheck disable=SC2016 -declare longDescription=''' -${__HELP_TITLE}NOTE:${__HELP_NORMAL} -the use of output, log-format, verbose options highly depends on the script used - -${__HELP_TITLE}LIST OF AVAILABLE DSN:${__HELP_NORMAL} -${dsnList} - -${__HELP_TITLE}DEFAULT QUERIES DIRECTORY:${__HELP_NORMAL} -${QUERIES_DIR-configuration error} - -${__HELP_TITLE}USER QUERIES DIRECTORY:${__HELP_NORMAL} -${HOME_QUERIES_DIR-configuration error} -Allows to override queries defined in "Default queries directory" - -${__HELP_TITLE}LIST OF AVAILABLE SCRIPTS:${__HELP_NORMAL} -${scriptsList} - -${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL} script conf/dbScripts/extractData.sh - executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using - ${__HELP_EXAMPLE}$0 -j 10 extractData databaseSize${__HELP_NORMAL} - - executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages) - ${__HELP_EXAMPLE}$0 -j 10 --log-format none extractData databaseSize${__HELP_NORMAL} - - use --verbose to get some debug information - ${__HELP_EXAMPLE}$0 -j 10 --log-format none --verbose extractData databaseSize${__HELP_NORMAL} - -${__HELP_TITLE}USE CASES:${__HELP_NORMAL} - you can use this script in order to check that each db model conforms with your ORM schema - simply create a new script in conf/dbQueries that will call your orm schema checker - - update multiple db at once (simple to complex update script) - -''' -declare defaultFromDsn="default.remote" -% - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.dsn.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.jobs.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.progressBar.tpl)" - -% -# shellcheck source=/dev/null -source <( - Options::generateGroup \ - --title "SCRIPT OPTIONS:" \ - --function-name groupSourceDbOptionsFunction - - argScriptToExecuteCallback() { :; } - Options::generateArg \ - --help "the script that will be executed on each databases" \ - --name 'scriptToExecute' \ - --min 1 \ - --max 1 \ - --variable-name "argScriptToExecute" \ - --callback argScriptToExecuteCallback \ - --function-name argScriptToExecuteFunction - - Options::generateArg \ - --help "optional parameters to pass to the script" \ - --name 'scriptArguments' \ - --min 0 \ - --max -1 \ - --variable-name "scriptArguments" \ - --function-name scriptArgumentsFunction - - # shellcheck disable=SC2116 - Options::generateOption \ - --help "if provided will check only this db, otherwise script will be executed on all dbs of mysql server" \ - --help-value-name "dbName" \ - --variable-type "StringArray" \ - --group groupSourceDbOptionsFunction \ - --alt "--database" \ - --variable-name "optionDatabases" \ - --function-name optionDatabasesFunction - - # shellcheck disable=SC2116 - Options::generateOption \ - --help "output directory, see log-format option" \ - --help-value-name "outputDirectory" \ - --variable-type "String" \ - --group groupSourceDbOptionsFunction \ - --alt "--output" \ - --alt "-o" \ - --callback outputDirectoryCallback \ - --variable-name "optionOutputDir" \ - --function-name optionOutputDirFunction - - # shellcheck disable=SC2116 - Options::generateOption \ - --help "if output dir provided, will log each db result to log file" \ - --help-value-name "logFormat" \ - --authorized-values "none|log" \ - --default-value "none" \ - --group groupSourceDbOptionsFunction \ - --alt "--log-format" \ - --alt "-l" \ - --variable-type "String" \ - --variable-name "optionLogFormat" \ - --function-name optionLogFormatFunction -) -options+=( - argScriptToExecuteFunction - scriptArgumentsFunction - optionDatabasesFunction - optionOutputDirFunction - optionLogFormatFunction - --callback dbScriptAllDatabasesCommandCallback -) -Options::generateCommand "${options[@]}" -% - -optionHelpCallback() { - local dsnList queriesList scriptsList - dsnList="$(Conf::getMergedList "dsn" "env")" - queriesList="$(Conf::getMergedList "dbQueries" "sql" || true)" - scriptsList="$(Conf::getMergedList "dbScripts" "sh")" - - <% ${commandFunctionName} %> help | envsubst - exit 0 -} - -outputDirectoryCallback() { - if [[ "${optionOutputDir:0:1}" != "/" ]]; then - # relative path - optionOutputDir="${PWD}/${optionOutputDir}" - fi - mkdir -p "${optionOutputDir}" || Log::fatal "unable to create directory ${optionOutputDir}" - if [[ ! -d "${optionOutputDir}" || ! -w "${optionOutputDir}" ]]; then - Log::fatal "output dir is not correct or not writable" - fi -} - -argScriptToExecuteCallback() { - if [[ ! -f "${argScriptToExecute}" ]]; then - declare scriptAbsoluteFile - scriptAbsoluteFile="$(Conf::getAbsoluteFile "dbScripts" "${argScriptToExecute}" "sh")" && { - argScriptToExecute="${scriptAbsoluteFile}" - if (( BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG )); then - Log::displayInfo "Using script file ${scriptAbsoluteFile}" - fi - } - fi -} - -dbScriptAllDatabasesCommandCallback() { - if [[ -z "${optionFromDsn}" ]]; then - # default value for FROM_DSN if from-aws not set - optionFromDsn="<% ${defaultFromDsn} %>" - fi -} - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.sh b/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.sh deleted file mode 100755 index 775d0ef8..00000000 --- a/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/dbScriptAllDatabases -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 - -# default values -declare outputDirectory="${HOME}/.bash-tools/output" -declare optionFromDsn="" -declare optionOutputDir="" -declare optionLogFormat="" -declare optionJobs="" -declare argScriptToExecute="" -declare -a scriptArguments=() - -# other configuration -declare copyrightBeginYear="2020" -declare QUERIES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbQueries" -declare HOME_QUERIES_DIR="${HOME}/.bash-tools/dbQueries" - -.INCLUDE "$(dynamicTemplateDir _binaries/DbScriptAllDatabases/dbScriptAllDatabases.options.tpl)" - -# @require Linux::requireExecutedAsUser -run() { - - # check dependencies - Assert::commandExists mysql "sudo apt-get install -y mysql-client" - Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" - Assert::commandExists parallel "sudo apt-get install -y parallel" - - # create db instance - declare -Agx dbInstance - Database::newInstance dbInstance "${optionFromDsn}" - Database::setQueryOptions dbInstance "${dbInstance['QUERY_OPTIONS']} --connect-timeout=5" - if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then - Log::displayInfo "Using dsn ${dbInstance['DSN_FILE']}" - fi - - # list of all databases - if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then - Log::displayInfo "get the list of all databases" - fi - - if ((${#optionDatabases[@]} == 0)); then - mapfile -t optionDatabases < <(Database::getUserDbList dbInstance) - fi - - if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then - Log::displayInfo "processing ${#optionDatabases[@]} databases using ${optionJobs} jobs" - fi - - export selectedQueryFile - export MYSQL_OPTIONS - - printf '%s\n' "${optionDatabases[@]}" | parallel --eta --progress --tag --jobs="${optionJobs}" \ - "${argScriptToExecute}" "${optionFromDsn}" "${optionLogFormat}" "${BASH_FRAMEWORK_ARGS_VERBOSE}" \ - "${optionOutputDir}" "${PWD}" "${scriptArguments[@]}" -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/DbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt b/src/_binaries/DbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt deleted file mode 100644 index be81fafe..00000000 --- a/src/_binaries/DbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt +++ /dev/null @@ -1,118 +0,0 @@ -DESCRIPTION: -Allows to execute a script on each database of specified mysql server -USAGE: dbScriptAllDatabases [OPTIONS] [ARGUMENTS] -USAGE: dbScriptAllDatabases [--jobs|-j ] [--bar|-b] - [--from-dsn|-f ] [--database ] [--output|-o ] - [--log-format|-l ] [--bash-framework-config ] [--config] - [--verbose|-v] [-vv] [-vvv] [--env-file ] [--no-color] - [--theme ] [--help|-h] [--version] [--quiet|-q] [--log-level ] - [--log-file ] [--display-level ] - -ARGUMENTS: - scriptToExecute {single} (mandatory) - the script that will be executed on each databases - [scriptArguments {list} (optional)] - optional parameters to pass to the script - -OPTIONS: - --jobs, -j  {single} - specify the number of db to query in parallel - Default value: 1 - --bar, -b {single} - Show progress as a progress bar. In the bar is shown: % of jobs completed, e - stimated seconds left, and number of jobs started. - -SCRIPT OPTIONS: - --from-dsn, -f  {single} - target mysql server - --database  {list} (optional) - if provided will check only this db, otherwise script will be executed on al - l dbs of mysql server - --output, -o  {single} - output directory, see log-format option - --log-format, -l  {single} - if output dir provided, will log each db result to log file - Default value: none - Possible values: none|log - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -NOTE: -the use of output, log-format, verbose options highly depends on the script used - -LIST OF AVAILABLE DSN: - - dsn_local - - dsn_valid - -DEFAULT QUERIES DIRECTORY: -/bash/conf/dbQueries - -USER QUERIES DIRECTORY: -home/.bash-tools/dbQueries -Allows to override queries defined in Default queries directory - -LIST OF AVAILABLE SCRIPTS: - - -EXAMPLES: script conf/dbScripts/extractData.sh - executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using - /bash/bin/dbScriptAllDatabases -j 10 extractData databaseSize - - executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages) - /bash/bin/dbScriptAllDatabases -j 10 --log-format none extractData databaseSize - - use --verbose to get some debug information - /bash/bin/dbScriptAllDatabases -j 10 --log-format none --verbose extractData databaseSize - -USE CASES: - you can use this script in order to check that each db model conforms with your ORM schema - simply create a new script in conf/dbQueries that will call your orm schema checker - - update multiple db at once (simple to complex update script) - -VERSION: 2.0 - -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.sh - -LICENSE: -MIT License - -Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/DbScriptAllDatabases/testsData/getUserDbList.query b/src/_binaries/DbScriptAllDatabases/testsData/getUserDbList.query deleted file mode 100644 index d889d8d2..00000000 --- a/src/_binaries/DbScriptAllDatabases/testsData/getUserDbList.query +++ /dev/null @@ -1 +0,0 @@ -SELECT `schema_name` from INFORMATION_SCHEMA.SCHEMATA WHERE `schema_name` NOT IN("information_schema", "mysql", "performance_schema", "sys") diff --git a/src/_binaries/commandDefinitions/optionsFromDsn.yaml b/src/_binaries/commandDefinitions/optionsFromDsn.yaml new file mode 100644 index 00000000..415204b8 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsFromDsn.yaml @@ -0,0 +1,16 @@ +--- +binData: + commands: + default: + optionGroups: + groupSourceDbOptions: + title: "SOURCE OPTIONS:" + options: + - variableName: optionFromDsn + group: groupSourceDbOptions + type: String + help: Target mysql server. + helpValueName: dsn + alts: + - --from-dsn + - -f diff --git a/src/_binaries/commandDefinitions/optionsJobs.sh b/src/_binaries/commandDefinitions/optionsJobs.sh index 3d16ea27..4551ee3f 100755 --- a/src/_binaries/commandDefinitions/optionsJobs.sh +++ b/src/_binaries/commandDefinitions/optionsJobs.sh @@ -1,5 +1,11 @@ #!/usr/bin/env bash +declare -a PARALLEL_OPTIONS + +optionProgressBarCallback() { + PARALLEL_OPTIONS+=(--bar) +} + optionJobsCallback() { # shellcheck disable=SC2154 if ! [[ "${optionJobs}" =~ ^[0-9]+$ ]]; then diff --git a/src/_binaries/commandDefinitions/optionsJobs.yaml b/src/_binaries/commandDefinitions/optionsJobs.yaml index b72c037b..d5352c4d 100644 --- a/src/_binaries/commandDefinitions/optionsJobs.yaml +++ b/src/_binaries/commandDefinitions/optionsJobs.yaml @@ -19,3 +19,15 @@ binData: alts: - --jobs - -j + + - variableName: optionProgressBar + group: groupJobOptions + type: Boolean + help: | + Show progress as a progress bar. In the bar is shown: % of jobs completed, + estimated seconds left, and number of jobs started. + callbacks: + - optionProgressBarCallback + alts: + - --bar + - -b diff --git a/src/_binaries/commandDefinitions/optionsProgressBar.sh b/src/_binaries/commandDefinitions/optionsProgressBar.sh deleted file mode 100755 index a576d471..00000000 --- a/src/_binaries/commandDefinitions/optionsProgressBar.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -declare -a PARALLEL_OPTIONS - -optionProgressBarCallback() { - PARALLEL_OPTIONS+=(--bar) -} diff --git a/src/_binaries/commandDefinitions/optionsProgressBar.yaml b/src/_binaries/commandDefinitions/optionsProgressBar.yaml deleted file mode 100644 index f9720872..00000000 --- a/src/_binaries/commandDefinitions/optionsProgressBar.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -binData: - commands: - default: - definitionFiles: - 27: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsProgressBar.sh" - optionGroups: - groupProgressBarOptions: - title: "PROGRESS BAR OPTIONS:" - options: - - variableName: optionProgressBar - group: groupProgressBarOptions - type: Boolean - help: | - Show progress as a progress bar. In the bar is shown: % of jobs completed, - estimated seconds left, and number of jobs started. - callbacks: - - optionProgressBarCallback - alts: - - --bar - - -b