diff --git a/.travis.yml b/.travis.yml index de85d19..6cdc7f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ before_script: - sudo bash -c 'echo example.com > /etc/hostname' - unset LANG - sudo apt-get update --allow-releaseinfo-change -qq - - sudo apt-get -qq purge mysql* graphviz* redis* + - sudo apt-get -qq purge mysql* graphviz* redis* php* - sudo apt-get -qq autoremove --purge diff --git a/CHANGELOG.md b/CHANGELOG.md index df274c7..85cc359 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,35 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### v3.9.x - [Unreleased] +### v3.11.0 - 2019-12-03 + +#### Added + +- PHP 7.4 support +- Improved Webp images support with Cloudflare (Issue [#95](https://github.com/WordOps/WordOps/issues/95)). Nginx will not serve webp images alternative with Cloudflare IP ranges. +- Stack upgrade for adminer +- Check acme.sh installation and setup acme.sh if needed before issuing certificate +- Add `--ufw` to `wo stack status` +- Add Nginx directive `gzip_static on;` to serve precompressed assets with Cache-Enabler or WP-Rocket. (Issue [#207](https://github.com/WordOps/WordOps/issues/207)) + +#### Changed + +- Previous `--php73` & `--php73=off` flags are replaced by `--php72`, `--php73`, `--php74` to switch site's php version +- phpMyAdmin updated to v4.9.2 +- Adminer updated to v4.7.5 +- Replace dot and dashes by underscores in database names (Issue [#206](https://github.com/WordOps/WordOps/issues/206)) +- Increased database name length to 32 characters from domain name + 8 random characters + +#### Fixed + +- typo error in motd-news script (Issue [#204](https://github.com/WordOps/WordOps/issues/204)) +- Install Nginx before ngxblocker +- WordOps install/update script text color +- Issue with MySQL stack on Raspbian 9/10 +- Typo error (PR [#205](https://github.com/WordOps/WordOps/pull/205)) +- php version in `wo debug` (PR [#209](https://github.com/WordOps/WordOps/pull/209)) +- SSL certificates expiration display with shared wildcard certificates + ### v3.10.3 - 2019-11-11 #### Added diff --git a/README.md b/README.md index 9b687ca..cbc2f51 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ - **Easy to install** : One step automated installer with migration from EasyEngine v3 support - **Fast deployment** : Fast and automated WordPress, Nginx, PHP, MySQL & Redis installation - **Custom Nginx build** : Nginx 1.16.1 - TLS v1.3 Cloudflare HTTP/2 HPACK & Brotli support -- **Up-to-date** : PHP 7.2 & 7.3, MariaDB 10.3 & Redis 5.0 +- **Up-to-date** : PHP 7.2, 7.3 & 7.4, MariaDB 10.3 & Redis 5.0 - **Secured** : Hardened WordPress security with strict Nginx location directives - **Powerful** : Optimized Nginx configurations with multiple cache backends support - **SSL** : Domain, Subdomain & Wildcard Let's Encrypt SSL certificates with DNS API support @@ -67,11 +67,10 @@ - Debian 9 (Stretch) - Debian 10 (Buster) - Raspbian 9 (Stretch) -- Raspbian 10 (Buster) - Testing +- Raspbian 10 (Buster) ## Getting Started - ```bash wget -qO wo wops.cc && sudo bash wo # Install WordOps sudo wo site create example.com --wp # Install required packages & setup WordPress on example.com @@ -86,6 +85,7 @@ Detailed Getting Started guide with additional installation methods can be found ```bash wo site create example.com --wp # install wordpress without any page caching wo site create example.com --wp --php73 # install wordpress with PHP 7.3 without any page caching +wo site create example.com --wp --php74 # install wordpress with PHP 7.4 without any page caching wo site create example.com --wpfc # install wordpress + nginx fastcgi_cache wo site create example.com --wpredis # install wordpress + nginx redis_cache wo site create example.com --wprocket # install wordpress with WP-Rocket plugin @@ -121,11 +121,21 @@ wo site create example.com --wpsubdomain --wpce # install wpmu-subdomain + C wo site create example.com --html # create example.com for static/html sites wo site create example.com --php # create example.com with php support wo site create example.com --php73 # create example.com with php 7.3 support +wo site create example.com --php73 # create example.com with php 7.4 support wo site create example.com --mysql # create example.com with php & mysql support wo site create example.com --mysql --php73 # create example.com with php 7.3 & mysql support +wo site create example.com --mysql --php74 # create example.com with php 7.4 & mysql support wo site create example.com --proxy=127.0.0.1:3000 # create example.com with nginx as reverse-proxy ``` +### Switch between PHP versions + +```bash +wo site update example/com --php72 # switch to PHP 7.2 +wo site update example.com --php73 # switch to PHP 7.3 +wo site update example.com --php74 # switch to PHP 7.4 +``` + ### Sites secured with Let's Encrypt ```bash @@ -148,7 +158,7 @@ For any other questions about WordOps or if you need support, please use the [Co # Contributing -If you'd like to contribute, please fork the repository and make changes as you'd like. Pull requests are warmly welcome. +If you'd like to contribute, please fork the reposi7tory and make changes as you'd like. Pull requests are warmly welcome. There is no need to be a developer or a system administrator to contribute to WordOps project. You can still contribute by helping us to improve [WordOps documentation](https://github.com/WordOps/docs.wordops.net). ## Credits diff --git a/config/bash_completion.d/wo_auto.rc b/config/bash_completion.d/wo_auto.rc index c3146c0..d76acf7 100644 --- a/config/bash_completion.d/wo_auto.rc +++ b/config/bash_completion.d/wo_auto.rc @@ -53,7 +53,7 @@ _wo_complete() "info") COMPREPLY=( $(compgen \ - -W "--mysql --php --php73 --nginx" \ + -W "--mysql --php --php73 --php74 --nginx" \ -- $cur) ) ;; @@ -74,17 +74,17 @@ _wo_complete() # HANDLE EVERYTHING AFTER THE THIRD LEVEL NAMESPACE "install" | "purge" | "remove" ) COMPREPLY=( $(compgen \ - -W "--web --admin --security --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --phpredisadmin --composer --netdata --fail2ban --ufw --dashboard --proftpd --clamav --ngxblocker --mysqlclient --mysqltuner --extplorer --all --force" \ + -W "--web --admin --security --nginx --php --php73 --php74 --mysql --wpcli --phpmyadmin --adminer --utils --redis --phpredisadmin --composer --netdata --fail2ban --ufw --dashboard --proftpd --clamav --sendmail --ngxblocker --mysqlclient --mysqltuner --extplorer --nanorc --cheat --all --force" \ -- $cur) ) ;; "upgrade" ) COMPREPLY=( $(compgen \ - -W "--web --admin --utils --nginx --php --php73 --mysql --all --netdata --composer --phpmyadmin --dashboard --mysqtuner --wpcli --force" \ + -W "--web --admin --utils --nginx --php --php73 --php74 --mysql --all --netdata --composer --phpmyadmin --adminer --dashboard --mysqtuner --wpcli --force" \ -- $cur) ) ;; "start" | "stop" | "reload" | "restart" | "status") COMPREPLY=( $(compgen \ - -W "--nginx --php --php73 --mysql --redis --fail2ban --ufw --netdata -proftpd" \ + -W "--nginx --php --php73 --php74 --mysql --redis --fail2ban --ufw --netdata -proftpd" \ -- $cur) ) ;; "list") @@ -154,13 +154,13 @@ _wo_complete() "create") COMPREPLY=( $(compgen \ - -W "--user --pass --email --html --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --proxy= --wpredis --wprocket --wpce -le --letsencrypt --letsencrypt=wildcard -le=wildcard --dns --dns=dns_cf --dns=dns_dgon" \ + -W "--user --pass --email --html --php --php73 --php74 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --proxy= --wpredis --wprocket --wpce -le --letsencrypt --letsencrypt=wildcard -le=wildcard --dns --dns=dns_cf --dns=dns_dgon" \ -- $cur) ) ;; "update") COMPREPLY=( $(compgen \ - -W "--password --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --wprocket --wpce -le -le=off --letsencrypt --letsencrypt=off --letsencrypt=clean -le=wildcard -le=clean --dns --dns=dns_cf --dns=dns_dgon --ngxblocker --ngxblocker=off" \ + -W "--password --php --php72 --php73 --php74 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --wprocket --wpce -le -le=off --letsencrypt --letsencrypt=off --letsencrypt=clean -le=wildcard -le=clean --dns --dns=dns_cf --dns=dns_dgon --ngxblocker --ngxblocker=off" \ -- $cur) ) ;; "delete") @@ -206,9 +206,9 @@ _wo_complete() "--wp") if [ "${COMP_WORDS[1]}" != "debug" ]; then if [ "${COMP_WORDS[2]}" == "create" ]; then - retlist="--wp --wpsc --wpfc --user --email --pass --wpredis --wprocket --wpce --letsencrypt -le --letsencrypt=wildcard --dns --dns=dns_cf --dns=dns_dgon --php73" + retlist="--wp --wpsc --wpfc --user --email --pass --wpredis --wprocket --wpce --letsencrypt -le --letsencrypt=wildcard --dns --dns=dns_cf --dns=dns_dgon --php73 --php74" elif [ "${COMP_WORDS[2]}" == "update" ]; then - retlist="--wp --wpfc --wpsc --php73 --php73=off --wpredis --wprocket --wpce -le --letsencrypt --letsencrypt=wildcard -le=wildcard --dns --dns=dns_cf --dns=dns_dgon" + retlist="--wp --wpfc --wpsc --php72 --php73 --php74 --wpredis --wprocket --wpce -le --letsencrypt --letsencrypt=wildcard -le=wildcard --dns --dns=dns_cf --dns=dns_dgon" else retlist="" fi @@ -225,9 +225,9 @@ _wo_complete() "--wpsubdir" | "--wpsubdomain") if [ "${COMP_WORDS[1]}" != "debug" ]; then if [ "${COMP_WORDS[2]}" == "create" ]; then - retlist="--wpsc --wpfc --user --email --pass --wpredis --wprocket --wpce -le -le=wildcard --letsencrypt --letsencrypt=wildcard --php73 --dns --dns=dns_cf --dns=dns_dgon" + retlist="--wpsc --wpfc --user --email --pass --wpredis --wprocket --wpce -le -le=wildcard --letsencrypt --letsencrypt=wildcard --php73 --php74 --dns --dns=dns_cf --dns=dns_dgon" elif [ "${COMP_WORDS[2]}" == "update" ]; then - retlist="--wpfc --wpsc --php73 --php73=off --wpredis --wprocket --wpce -le -le=wildcard --letsencrypt --letsencrypt=wildcard --dns --dns=dns_cf --dns=dns_dgon" + retlist="--wpfc --wpsc --php72 --php73 --php74 --wpredis --wprocket --wpce -le -le=wildcard --letsencrypt --letsencrypt=wildcard --dns --dns=dns_cf --dns=dns_dgon" else retlist="" fi @@ -243,7 +243,7 @@ _wo_complete() "--wpredis" | "--wprocket" | "--wpce" | "--wpfc" | "--wpsc" | "--wpsubdir" | "--wpsubdomain" | "--user" | "--pass" | "--email" | "--wp") if [ "${COMP_WORDS[2]}" == "create" ]; then - retlist="--user --pass --email --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --wprocket --wpce --php73 -le -le=wildcard --letsencrypt --letsencrypt=wildcard --dns --dns=dns_cf --dns=dns_dgon" + retlist="--user --pass --email --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --wprocket --wpce --php73 --php74 -le -le=wildcard --letsencrypt --letsencrypt=wildcard --dns --dns=dns_cf --dns=dns_dgon" else retlist="" fi @@ -254,9 +254,9 @@ _wo_complete() -- $cur) ) ;; - "--wpredis" | "--wprocket" | "--wpce" | "--wpfc") + "--wpredis" | "--wprocket" | "--wpce" | "--wpfc" | "--wpsc") if [ "${COMP_WORDS[2]}" == "update" ]; then - retlist="--password --php --php73 --mysql --wp --wpsubdir --wpsubdomain -le --letsencrypt --dns --dns=dns_cf --dns=dns_dgon" + retlist="--password --php72 --php73 --php74 --mysql --wp --wpsubdir --wpsubdomain -le --letsencrypt --dns --dns=dns_cf --dns=dns_dgon" else retlist="" fi @@ -267,11 +267,11 @@ _wo_complete() -- $cur) ) ;; - "--web" | "--admin" | "--nginx" | "--php" | "--php73" | "--mysql" | "--wpcli" | "--phpmyadmin" | "--adminer" | "--utils" | "--fail2ban" | "--ufw" | "--redis | --phpredisadmin | --netdata") + "--web" | "--admin" | "--nginx" | "--php" | "--php73" | "--php74" | "--mysql" | "--wpcli" | "--phpmyadmin" | "--adminer" | "--utils" | "--fail2ban" | "--ufw" | "--redis" | "--phpredisadmin" | "--netdata" | "--sendmail" | "--composer" | "--proftpd" | "--cheat" | "--nanorc" | "--clamav") if [[ "${COMP_WORDS[2]}" == "install" || "${COMP_WORDS[2]}" == "purge" || "${COMP_WORDS[2]}" == "remove" ]]; then - retlist="--web --admin --security --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --fail2ban --ufw --phpredisadmin --netdata --force" + retlist="--web --admin --security --nginx --php --php73 --php74 --mysql --wpcli --phpmyadmin --adminer --utils --redis --fail2ban --ufw --phpredisadmin --netdata --force" elif [[ "${COMP_WORDS[2]}" == "start" || "${COMP_WORDS[2]}" == "reload" || "${COMP_WORDS[2]}" == "restart" || "${COMP_WORDS[2]}" == "stop" ]]; then - retlist="--nginx --php --php73 --mysql --redis --netdata --fail2ban --ufw" + retlist="--nginx --php --php73 --php74 --mysql --redis --netdata --fail2ban --ufw" elif [[ "${COMP_WORDS[1]}" == "debug" ]]; then retlist="--start --nginx --php --php73 --fpm --fpm7 --mysql -i --interactive -stop --import-slow-log --import-slow-log-interval= -" if [[ $prev == '--mysql' ]]; then @@ -326,8 +326,8 @@ _wo_complete() -W "$(echo $ret)" \ -- $cur) ) ;; - "--auth" | "--port" | "--ip") - retlist="--auth --port --ip" + "--auth" | "--port" | "--ip" | "--ssh" | "--sshport") + retlist="--auth --port --ip --ssh --sshport" ret="${retlist[@]/$prev}" COMPREPLY=( $(compgen \ -W "$(echo $ret)" \ @@ -358,7 +358,7 @@ _wo_complete() case "$mprev" in "--user" | "--email" | "--pass") if [ "${COMP_WORDS[2]}" == "create" ]; then - retlist="--user --pass --email --html --php --php73 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --wprocket --wpce -le -le=wildcard --letsencrypt --letsencrypt=wildcard --dns --dns=dns_cf --dns=dns_dgon" + retlist="--user --pass --email --html --php --php73 --php74 --mysql --wp --wpsubdir --wpsubdomain --wpfc --wpsc --wpredis --wprocket --wpce -le -le=wildcard --letsencrypt --letsencrypt=wildcard --dns --dns=dns_cf --dns=dns_dgon" fi ret="${retlist[@]/$prev}" COMPREPLY=( $(compgen \ diff --git a/config/wo.conf b/config/wo.conf index f0d077c..5fcf7f6 100644 --- a/config/wo.conf +++ b/config/wo.conf @@ -18,7 +18,7 @@ # template_dir = /var/lib/wo/templates/ -[log.logging] +[log.colorlog] ### Where the log file lives (no log file by default) file = /var/log/wo/wordops.log @@ -38,6 +38,10 @@ max_bytes = 1000000 ### The maximun number of log files to maintain when rotating max_files = 7 +colorize_file_log = true + +colorize_console_log = true + [stack] ### IP address that will be used in Nginx configurations while installing diff --git a/install b/install index 39ca2e0..6c6e4a6 100755 --- a/install +++ b/install @@ -9,7 +9,7 @@ # ------------------------------------------------------------------------- # wget -qO wo wops.cc && sudo bash wo # ------------------------------------------------------------------------- -# Version 3.10.3 - 2019-11-11 +# Version 3.11.0 - 2019-12-03 # ------------------------------------------------------------------------- # CONTENTS @@ -23,25 +23,25 @@ # 1 - Set the CLI output colors ### -TPUT_RESET=$(tput sgr0) -TPUT_FAIL=$(tput setaf 1) -TPUT_INFO=$(tput setaf 7) -TPUT_ECHO=$(tput setaf 4) -TPUT_OK=$(tput setaf 2) +CSI='\033[' +TPUT_RESET="${CSI}0m" +TPUT_FAIL="${CSI}1;31m" +TPUT_ECHO="${CSI}1;36m" +TPUT_OK="${CSI}1;32m" wo_lib_echo() { - echo "${TPUT_ECHO}${*}${TPUT_RESET}" + echo -e "${TPUT_ECHO}${*}${TPUT_RESET}" } wo_lib_echo_info() { - echo "${TPUT_INFO}${*}${TPUT_RESET}" + echo -e "$*" } wo_lib_echo_fail() { - echo "${TPUT_FAIL}${*}${TPUT_RESET}" + echo -e "${TPUT_FAIL}${*}${TPUT_RESET}" } ### @@ -49,7 +49,7 @@ wo_lib_echo_fail() { ### wo_lib_error() { - echo "[ $(date) ] ${TPUT_FAIL}${*}${TPUT_RESET}" + echo -e "[ $(date) ] ${TPUT_FAIL}${*}${TPUT_RESET}" exit "$2" } @@ -169,7 +169,8 @@ wo_check_distro() { else check_wo_linux_distro=$(lsb_release -sc | grep -E "xenial|bionic|disco|jessie|stretch|buster") if [ -z "$check_wo_linux_distro" ]; then - wo_lib_echo_fail "WordOps (wo) only supports Ubuntu 16.04/18.04/19.04 LTS, Debian 9.x/10.x and Raspbian 9.x/10x" + wo_lib_echo_fail "WordOps (wo) only supports Ubuntu 16.04/18.04/19.04 LTS, Debian 9.x/10.x and Raspbian 9.x/10x.\n + You can bypass this warning by adding the flag --force to the install command" exit 100 fi fi @@ -426,8 +427,10 @@ wo_install_acme_sh() { wo_install() { cd /usr/local/lib/python3.*/dist-packages || exit 1 if [ "$wo_branch" = "master" ]; then + python3 -m pip uninstall wo -y python3 -m pip install --upgrade wordops else + python3 -m pip uninstall wo -y python3 -m pip install -U "git+git://github.com/WordOps/WordOps.git@$wo_branch#egg=wordops" fi cp -rf /usr/local/lib/python3.*/dist-packages/usr/* /usr/ @@ -594,8 +597,7 @@ wo_domain_suffix() { } wo_clean() { - echo "pass" - + rm -rf /usr/local/lib/python3.*/dist-packages/{wo-*.egg,cement-*.egg,wordops-*.egg} } wo_uninstall() { @@ -623,6 +625,14 @@ wo_clean_repo() { fi } +wo_woconf() { + if [ -f /etc/wo/wo.conf ]; then + if grep -q "log.logging" /etc/wo/wo.conf; then + sed -i "s/log.logging/log.colorlog/g" /etc/wo/wo.conf + fi + fi +} + wo_init() { ### @@ -631,12 +641,12 @@ wo_init() { if [ -z "$wo_travis" ]; then # import easyengine opensusebuildservice gpg key to avoid issues with packages update - apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3050ac3cd2ae6f03 > /dev/null 2>&1 - apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0xF1656F24C74CD1D8 > /dev/null 2>&1 + apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3050ac3cd2ae6f03 >/dev/null 2>&1 + apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0xF1656F24C74CD1D8 >/dev/null 2>&1 # fix digitalocean mariadb repository issue - sed -i 's/sfo1.mirrors.digitalocean.com\/mariadb/mariadb.mirrors.ovh.net\/MariaDB/' /etc/apt/sources.list.d/*.list > /dev/null 2>&1 - if [ -f /etc/apt/preferences.d/MariaDB.pref ]; then - sed -i 's/sfo1.mirrors.digitalocean.com/mariadb.mirrors.ovh.net/' /etc/apt/preferences.d/MariaDB.pref > /dev/null 2>&1 + sed -i 's/sfo1.mirrors.digitalocean.com\/mariadb/mariadb.mirrors.ovh.net\/MariaDB/' /etc/apt/sources.list.d/*.list >/dev/null 2>&1 + if [ -f /etc/apt/preferences.d/MariaDB.pref ]; then + sed -i 's/sfo1.mirrors.digitalocean.com/mariadb.mirrors.ovh.net/' /etc/apt/preferences.d/MariaDB.pref >/dev/null 2>&1 fi if ! { apt-get update --allow-releaseinfo-change -qq >/dev/null 2>&1 @@ -696,16 +706,20 @@ else # 1 - WO already installed if [ -x /usr/local/bin/wo ]; then _run wo_clean - # 2 - Migration from EEv3 - elif [ -x /usr/local/bin/ee ]; then - if [ -z "$wo_force_install" ]; then - echo -e "Migrate from EasyEngine to WordOps (y/n): " && read -r WO_ANSWER - if [ "$WO_ANSWER" != "y" ] && [ "$WO_ANSWER" != "Y" ]; then - wo_lib_error "Not installing WordOps" 1 + _run wo_woconf + # 2 - Migration from EEv3 + else + if [ -x /usr/local/bin/ee ]; then + if [ -z "$wo_force_install" ]; then + echo -e "Migrate from EasyEngine to WordOps (y/n): " && read -r WO_ANSWER + if [ "$WO_ANSWER" != "y" ] && [ "$WO_ANSWER" != "Y" ]; then + wo_lib_error "Not installing WordOps" 1 + fi fi + _run wo_backup_ee "Backing-up EE install" + _run wo_remove_ee_cron "Removing EasyEngine cronjob" fi - _run wo_backup_ee "Backing-up EE install" - _run wo_remove_ee_cron "Removing EasyEngine cronjob" + fi _run wo_install_dep "Installing wo dependencies" _run wo_timesync @@ -737,10 +751,10 @@ else elif [ "$wo_upgrade" = "1" ]; then wo_lib_echo "WordOps (wo) upgrade to $wo_version_new was succesfull!" echo - wo_lib_echo "To upgrade WordOps web stacks use the command:" + wo_lib_echo "To upgrade WordOps web stacks, you can use the command:" wo_lib_echo_info "wo stack upgrade" echo - wo_lib_echo "To update all other packages use the command:" + wo_lib_echo "To update all other packages, you can use the command:" wo_lib_echo_info "wo maintenance" else wo_lib_echo "WordOps (wo) installed successfully" diff --git a/requirements.txt b/requirements.txt index 85341d9..1be986e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,6 @@ psutil>=5.6.3 sh>=1.12.14 SQLAlchemy>=1.3.8 requests>=2.22.0 -distro>=1.4.0 \ No newline at end of file +distro>=1.4.0 +argcomplete>=1.10.0 +colorlog>=4.0.2 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 514f780..242fed0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -15,7 +15,7 @@ where=tests/ license-file = LICENSE [flake8] -ignore = F405,W504,S322,S404,S603,s607,s602 +ignore = F405,W504,S322,S404,S603,s607,s602,C901 exclude = # No need to traverse our git directory .git, diff --git a/setup.py b/setup.py index 9ebae61..6af933c 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ if os.geteuid() == 0: os.makedirs('/var/lib/wo/tmp/') setup(name='wordops', - version='3.10.3', + version='3.11.0', description='An essential toolset that eases server administration', long_description=LONG, long_description_content_type='text/markdown', @@ -40,11 +40,17 @@ setup(name='wordops', "Natural Language :: English", "Topic :: System :: Systems Administration", ], - keywords='', + keywords='nginx automation wordpress deployment CLI', author='WordOps', author_email='contact@wordops.io', url='https://github.com/WordOps/WordOps', license='MIT', + project_urls={ + 'Documentation': 'https://docs.wordops.net', + 'Forum': 'https://community.wordops.net', + 'Source': 'https://github.com/WordOps/WordOps', + 'Tracker': 'https://github.com/WordOps/WordOps/issues', + }, packages=find_packages(exclude=['ez_setup', 'examples', 'tests', 'templates']), include_package_data=True, @@ -64,6 +70,8 @@ setup(name='wordops', 'SQLAlchemy >= 1.3.8', 'requests >= 2.22.0', 'distro >= 1.4.0', + 'argcomplete >= 1.10.0', + 'colorlog >= 4.0.2', ], extras_require={ # Optional 'testing': ['nose', 'coverage'], diff --git a/tests/cli/13_test_stack_install.py b/tests/cli/13_test_stack_install.py index b7a607c..1423a19 100644 --- a/tests/cli/13_test_stack_install.py +++ b/tests/cli/13_test_stack_install.py @@ -2,11 +2,7 @@ from wo.utils import test from wo.cli.main import WOTestApp -class CliTestCaseStack(test.WOTestCase): - - def test_wo_cli(self): - with WOTestApp as app: - app.run() +class CliTestCaseStackInstall(test.WOTestCase): def test_wo_cli_stack_install_nginx(self): with WOTestApp(argv=['stack', 'install', '--nginx']) as app: diff --git a/tests/cli/14_test_stack_services_stop.py b/tests/cli/14_test_stack_services_stop.py index 05897c6..a77db86 100644 --- a/tests/cli/14_test_stack_services_stop.py +++ b/tests/cli/14_test_stack_services_stop.py @@ -4,10 +4,6 @@ from wo.cli.main import WOTestApp class CliTestCaseStackStop(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_stack_services_stop_nginx(self): with WOTestApp(argv=['stack', 'stop', '--nginx']) as app: app.run() diff --git a/tests/cli/15_test_stack_services_start.py b/tests/cli/15_test_stack_services_start.py index 37e7f81..7241d5a 100644 --- a/tests/cli/15_test_stack_services_start.py +++ b/tests/cli/15_test_stack_services_start.py @@ -4,10 +4,6 @@ from wo.cli.main import WOTestApp class CliTestCaseStackStart(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_stack_services_start_nginx(self): with WOTestApp(argv=['stack', 'start', '--nginx']) as app: app.run() diff --git a/tests/cli/16_test_stack_services_restart.py b/tests/cli/16_test_stack_services_restart.py index c603ba7..531dabd 100644 --- a/tests/cli/16_test_stack_services_restart.py +++ b/tests/cli/16_test_stack_services_restart.py @@ -4,10 +4,6 @@ from wo.cli.main import WOTestApp class CliTestCaseStackRestart(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_stack_services_restart_nginx(self): with WOTestApp(argv=['stack', 'restart', '--nginx']) as app: app.run() diff --git a/tests/cli/17_test_stack_services_status.py b/tests/cli/17_test_stack_services_status.py index dda9b6a..7dee849 100644 --- a/tests/cli/17_test_stack_services_status.py +++ b/tests/cli/17_test_stack_services_status.py @@ -4,10 +4,6 @@ from wo.cli.main import WOTestApp class CliTestCaseStackStatus(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_stack_services_status_nginx(self): with WOTestApp(argv=['stack', 'status', '--nginx']) as app: app.run() diff --git a/tests/cli/18_test_site_create.py b/tests/cli/18_test_site_create.py index 2251cb8..b888c81 100644 --- a/tests/cli/18_test_site_create.py +++ b/tests/cli/18_test_site_create.py @@ -4,47 +4,53 @@ from wo.cli.main import WOTestApp class CliTestCaseSiteCreate(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_site_create_html(self): - with WOTestApp(argv=['site', 'create', 'example1.com', + with WOTestApp(argv=['site', 'create', 'html.com', '--html']) as app: app.config.set('wo', '', True) app.run() def test_wo_cli_site_create_php(self): - with WOTestApp(argv=['site', 'create', 'example2.com', + with WOTestApp(argv=['site', 'create', 'php.com', '--php']) as app: app.run() def test_wo_cli_site_create_mysql(self): - with WOTestApp(argv=['site', 'create', 'example3.com', + with WOTestApp(argv=['site', 'create', 'mysql.com', '--mysql']) as app: app.run() def test_wo_cli_site_create_wp(self): - with WOTestApp(argv=['site', 'create', 'example4.com', + with WOTestApp(argv=['site', 'create', 'wp.com', '--wp']) as app: app.run() def test_wo_cli_site_create_wpsubdir(self): - with WOTestApp(argv=['site', 'create', 'example5.com', + with WOTestApp(argv=['site', 'create', 'wpsubdir.com', '--wpsubdir']) as app: app.run() def test_wo_cli_site_create_wpsubdomain(self): - with WOTestApp(argv=['site', 'create', 'example6.com', + with WOTestApp(argv=['site', 'create', 'wpsubdomain.com', '--wpsubdomain']) as app: app.run() def test_wo_cli_site_create_wpfc(self): - with WOTestApp(argv=['site', 'create', 'example8.com', + with WOTestApp(argv=['site', 'create', 'wpfc.com', '--wpfc']) as app: app.run() def test_wo_cli_site_create_wpsc(self): - with WOTestApp(argv=['site', 'create', 'example9.com', + with WOTestApp(argv=['site', 'create', 'wpsc.com', '--wpsc']) as app: app.run() + + def test_wo_cli_site_create_wpce(self): + with WOTestApp(argv=['site', 'create', 'wpce.com', + '--wpce']) as app: + app.run() + + def test_wo_cli_site_create_wprocket(self): + with WOTestApp(argv=['site', 'create', 'wprocket.com', + '--wprocket']) as app: + app.run() diff --git a/tests/cli/19_test_site_disable.py b/tests/cli/19_test_site_disable.py index ad16c97..2cec395 100644 --- a/tests/cli/19_test_site_disable.py +++ b/tests/cli/19_test_site_disable.py @@ -4,10 +4,6 @@ from wo.cli.main import WOTestApp class CliTestCaseSiteDisable(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_site_disable(self): - with WOTestApp(argv=['site', 'disable', 'example2.com']) as app: + with WOTestApp(argv=['site', 'disable', 'html.com']) as app: app.run() diff --git a/tests/cli/20_test_site_enable.py b/tests/cli/20_test_site_enable.py index d829431..c50f31e 100644 --- a/tests/cli/20_test_site_enable.py +++ b/tests/cli/20_test_site_enable.py @@ -4,10 +4,6 @@ from wo.cli.main import WOTestApp class CliTestCaseSiteEnable(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_site_enable(self): - with WOTestApp(argv=['site', 'enable', 'example2.com']) as app: + with WOTestApp(argv=['site', 'enable', 'html.com']) as app: app.run() diff --git a/tests/cli/21_test_site_info.py b/tests/cli/21_test_site_info.py index c96f9a8..8ae39c0 100644 --- a/tests/cli/21_test_site_info.py +++ b/tests/cli/21_test_site_info.py @@ -4,10 +4,6 @@ from wo.cli.main import WOTestApp class CliTestCaseSiteInfo(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_site_info(self): - with WOTestApp(argv=['site', 'info', 'example1.com']) as app: + with WOTestApp(argv=['site', 'info', 'html.com']) as app: app.run() diff --git a/tests/cli/22_test_site_list.py b/tests/cli/22_test_site_list.py index 33edbf4..ba8a916 100644 --- a/tests/cli/22_test_site_list.py +++ b/tests/cli/22_test_site_list.py @@ -4,10 +4,6 @@ from wo.cli.main import WOTestApp class CliTestCaseSiteList(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_site_list_enable(self): with WOTestApp(argv=['site', 'list', '--enabled']) as app: app.run() diff --git a/tests/cli/23_test_site_show.py b/tests/cli/23_test_site_show.py index 5e00df3..757739c 100644 --- a/tests/cli/23_test_site_show.py +++ b/tests/cli/23_test_site_show.py @@ -4,10 +4,6 @@ from wo.cli.main import WOTestApp class CliTestCaseSiteShow(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_show_edit(self): - with WOTestApp(argv=['site', 'show', 'example1.com']) as app: + with WOTestApp(argv=['site', 'show', 'html.com']) as app: app.run() diff --git a/tests/cli/24_test_site_update.py b/tests/cli/24_test_site_update.py index ca34dc0..7536340 100644 --- a/tests/cli/24_test_site_update.py +++ b/tests/cli/24_test_site_update.py @@ -4,46 +4,42 @@ from wo.cli.main import WOTestApp class CliTestCaseSiteUpdate(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_site_update_html(self): - with WOTestApp(argv=['site', 'update', 'example2.com', + with WOTestApp(argv=['site', 'update', 'php.com', '--html']) as app: app.run() def test_wo_cli_site_update_php(self): - with WOTestApp(argv=['site', 'update', 'example1.com', + with WOTestApp(argv=['site', 'update', 'html.com', '--php']) as app: app.run() def test_wo_cli_site_update_mysql(self): - with WOTestApp(argv=['site', 'update', 'example1.com', + with WOTestApp(argv=['site', 'update', 'mysql.com', '--html']) as app: app.run() def test_wo_cli_site_update_wp(self): - with WOTestApp(argv=['site', 'update', 'example5.com', + with WOTestApp(argv=['site', 'update', 'mysql.com', '--wp']) as app: app.run() def test_wo_cli_site_update_wpsubdir(self): - with WOTestApp(argv=['site', 'update', 'example4.com', + with WOTestApp(argv=['site', 'update', 'wp.com', '--wpsubdir']) as app: app.run() def test_wo_cli_site_update_wpsubdomain(self): - with WOTestApp(argv=['site', 'update', 'example7.com', + with WOTestApp(argv=['site', 'update', 'wpsubdir.com', '--wpsubdomain']) as app: app.run() def test_wo_cli_site_update_wpfc(self): - with WOTestApp(argv=['site', 'update', 'example9.com', + with WOTestApp(argv=['site', 'update', 'wpsc.com', '--wpfc']) as app: app.run() def test_wo_cli_site_update_wpsc(self): - with WOTestApp(argv=['site', 'update', 'example6.com', + with WOTestApp(argv=['site', 'update', 'wpfc.com', '--wpsc']) as app: app.run() diff --git a/tests/cli/25_test_clean.py b/tests/cli/25_test_clean.py index 42e7935..c968528 100644 --- a/tests/cli/25_test_clean.py +++ b/tests/cli/25_test_clean.py @@ -4,10 +4,6 @@ from wo.cli.main import WOTestApp class CliTestCaseClean(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_clean(self): with WOTestApp(argv=['clean']) as app: app.run() diff --git a/tests/cli/26_test_debug.py b/tests/cli/26_test_debug.py deleted file mode 100644 index d2bd172..0000000 --- a/tests/cli/26_test_debug.py +++ /dev/null @@ -1,66 +0,0 @@ -from wo.utils import test -from wo.cli.main import WOTestApp - - -class CliTestCaseDebug(test.WOTestCase): - - def test_wo_cli(self): - with WOTestApp as app: - app.run() - - def test_wo_cli_debug_stop(self): - with WOTestApp(argv=['debug', '--stop']) as app: - app.run() - - def test_wo_cli_debug_start(self): - with WOTestApp(argv=['debug', '--start']) as app: - app.run() - - def test_wo_cli_debug_php(self): - with WOTestApp(argv=['debug', '--php']) as app: - app.run() - - def test_wo_cli_debug_nginx(self): - with WOTestApp(argv=['debug', '--nginx']) as app: - app.run() - - def test_wo_cli_debug_rewrite(self): - with WOTestApp(argv=['debug', '--rewrite']) as app: - app.run() - - def test_wo_cli_debug_fpm(self): - with WOTestApp(argv=['debug', '--fpm']) as app: - app.run() - - def test_wo_cli_debug_mysql(self): - with WOTestApp(argv=['debug', '--mysql']) as app: - app.run() - - def test_wo_cli_debug_import_slow_log_interval(self): - with WOTestApp(argv=['debug', '--mysql', - '--import-slow-log-interval']) as app: - app.run() - - def test_wo_cli_debug_site_name_mysql(self): - with WOTestApp(argv=['debug', 'example3.com', '--mysql']) as app: - app.run() - - def test_wo_cli_debug_site_name_wp(self): - with WOTestApp(argv=['debug', 'example4.com', '--wp']) as app: - app.run() - - def test_wo_cli_debug_site_name_nginx(self): - with WOTestApp(argv=['debug', 'example4.com', '--nginx']) as app: - app.run() - - def test_wo_cli_debug_site_name_start(self): - with WOTestApp(argv=['debug', 'example1.com', '--start']) as app: - app.run() - - def test_wo_cli_debug_site_name_stop(self): - with WOTestApp(argv=['debug', 'example1.com', '--stop']) as app: - app.run() - - def test_wo_cli_debug_site_name_rewrite(self): - with WOTestApp(argv=['debug', 'example1.com', '--rewrite']) as app: - app.run() diff --git a/tests/cli/27_test_info.py b/tests/cli/27_test_info.py index 5a6816c..b16047d 100644 --- a/tests/cli/27_test_info.py +++ b/tests/cli/27_test_info.py @@ -4,10 +4,6 @@ from wo.cli.main import WOTestApp class CliTestCaseInfo(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_info_mysql(self): with WOTestApp(argv=['info', '--mysql']) as app: app.run() diff --git a/tests/cli/28_test_secure.py b/tests/cli/28_test_secure.py index 4500a2a..e645978 100644 --- a/tests/cli/28_test_secure.py +++ b/tests/cli/28_test_secure.py @@ -4,10 +4,6 @@ from wo.cli.main import WOTestApp class CliTestCaseSecure(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_secure_auth(self): with WOTestApp(argv=['secure', '--auth', 'abc', 'superpass']) as app: app.run() diff --git a/tests/cli/29_test_site_delete.py b/tests/cli/29_test_site_delete.py index 38cf15a..ced17dd 100644 --- a/tests/cli/29_test_site_delete.py +++ b/tests/cli/29_test_site_delete.py @@ -4,26 +4,22 @@ from wo.cli.main import WOTestApp class CliTestCaseSiteDelete(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_site_detele(self): - with WOTestApp(argv=['site', 'delete', 'example1.com', - '--no-prompt']) as app: + with WOTestApp(argv=['site', 'delete', 'html.com', + '--force']) as app: app.run() def test_wo_cli_site_detele_all(self): - with WOTestApp(argv=['site', 'delete', 'example2.com', - '--all', '--no-prompt']) as app: + with WOTestApp(argv=['site', 'delete', 'wp.com', + '--all', '--force']) as app: app.run() def test_wo_cli_site_detele_db(self): - with WOTestApp(argv=['site', 'delete', 'example3.com', - '--db', '--no-prompt']) as app: + with WOTestApp(argv=['site', 'delete', 'mysql.com', + '--db', '--force']) as app: app.run() def test_wo_cli_site_detele_files(self): - with WOTestApp(argv=['site', 'delete', 'example4.com', - '--files', '--no-prompt']) as app: + with WOTestApp(argv=['site', 'delete', 'php.com', + '--files', '--force']) as app: app.run() diff --git a/tests/cli/30_test_stack_remove.py b/tests/cli/30_test_stack_remove.py index d952055..c7ca64a 100644 --- a/tests/cli/30_test_stack_remove.py +++ b/tests/cli/30_test_stack_remove.py @@ -4,10 +4,6 @@ from wo.cli.main import WOTestApp class CliTestCaseStackRemove(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_stack_remove_admin(self): with WOTestApp(argv=['stack', 'remove', '--admin', '--force']) as app: app.run() diff --git a/tests/cli/31_test_stack_purge.py b/tests/cli/31_test_stack_purge.py index eaef5d7..2420bae 100644 --- a/tests/cli/31_test_stack_purge.py +++ b/tests/cli/31_test_stack_purge.py @@ -4,10 +4,6 @@ from wo.cli.main import WOTestApp class CliTestCaseStackPurge(test.WOTestCase): - def test_wo_cli(self): - with WOTestApp as app: - app.run() - def test_wo_cli_stack_purge_web(self): with WOTestApp( argv=['stack', 'purge', '--web', '--force']) as app: diff --git a/tests/travis.sh b/tests/travis.sh index 40abaea..b0754fb 100644 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -16,7 +16,7 @@ export LC_ALL='C.UTF-8' if [ -z "$1" ]; then { - apt-get -qq purge mysql* graphviz* redis* + apt-get -qq purge mysql* graphviz* redis* php73-* php-* apt-get install -qq git python3-setuptools python3-dev python3-apt ccze tree sudo apt-get -qq autoremove --purge } > /dev/null 2>&1 @@ -30,7 +30,7 @@ exit_script() { echo -e "${CGREEN}#############################################${CEND}" echo -e ' stack install ' echo -e "${CGREEN}#############################################${CEND}" -stack_list='nginx php php73 mysql redis fail2ban clamav proftpd netdata phpmyadmin composer dashboard extplorer adminer redis phpredisadmin mysqltuner utils ufw ngxblocker cheat nanorc' +stack_list='nginx php php73 php74 mysql redis fail2ban clamav proftpd netdata phpmyadmin composer dashboard extplorer adminer redis phpredisadmin mysqltuner utils ufw ngxblocker cheat nanorc' for stack in $stack_list; do echo -ne " Installing $stack [..]\r" if { @@ -49,7 +49,7 @@ done echo -e "${CGREEN}#############################################${CEND}" echo -e ' Simple site create ' echo -e "${CGREEN}#############################################${CEND}" -site_types='html php php73 mysql wp wpfc wpsc wpredis wpce wprocket wpsubdomain wpsubdir ngxblocker' +site_types='html php php73 php74 mysql wp wpfc wpsc wpredis wpce wprocket wpsubdomain wpsubdir ngxblocker' for site in $site_types; do echo -ne " Creating $site [..]\r" if { @@ -64,10 +64,16 @@ for site in $site_types; do fi done +echo +echo -e "${CGREEN}#############################################${CEND}" +echo +wo site info wp.net +echo + echo -e "${CGREEN}#############################################${CEND}" echo -e ' wo site update --php73 ' echo -e "${CGREEN}#############################################${CEND}" -other_site_types='html mysql wp wpfc wpsc wpredis wpce wprocket wpsubdomain wpsubdir ngxblocker' +other_site_types='html mysql php php74 wp wpfc wpsc wpredis wpce wprocket wpsubdomain wpsubdir ngxblocker' for site in $other_site_types; do echo -ne " Updating site to $site php73 [..]\r" if { @@ -82,7 +88,57 @@ for site in $other_site_types; do fi done +echo +echo -e "${CGREEN}#############################################${CEND}" +echo +wo site info wp.net +echo +echo -e "${CGREEN}#############################################${CEND}" +echo -e ' wo site update --php74 ' +echo -e "${CGREEN}#############################################${CEND}" +other_site_types='html mysql wp php php73 wpfc wpsc wpredis wpce wprocket wpsubdomain wpsubdir ngxblocker' +for site in $other_site_types; do + echo -ne " Updating site to $site php74 [..]\r" + if { + wo site update ${site}.net --php74 + } >>/var/log/wo/test.log; then + echo -ne " Updating site to $site php74 [${CGREEN}OK${CEND}]\\r" + echo -ne '\n' + else + echo -e " Updating site to $site php74 [${CRED}FAIL${CEND}]" + echo -ne '\n' + exit_script + fi +done +echo +echo -e "${CGREEN}#############################################${CEND}" +echo +wo site info wp.net +echo +echo -e "${CGREEN}#############################################${CEND}" +echo -e ' wo site update --php72 ' +echo -e "${CGREEN}#############################################${CEND}" +other_site_types='html mysql php php73 php74 wp wpfc wpsc wpredis wpce wprocket wpsubdomain wpsubdir ngxblocker' +for site in $other_site_types; do + echo -ne " Updating site to $site php72 [..]\r" + if { + wo site update ${site}.net --php72 + } >>/var/log/wo/test.log; then + echo -ne " Updating site to $site php72 [${CGREEN}OK${CEND}]\\r" + echo -ne '\n' + else + echo -e " Updating site to $site php72 [${CRED}FAIL${CEND}]" + echo -ne '\n' + exit_script + + fi +done +echo +echo -e "${CGREEN}#############################################${CEND}" +echo +wo site info wp.net +echo echo -e "${CGREEN}#############################################${CEND}" echo -e ' wo site update WP ' echo -e "${CGREEN}#############################################${CEND}" @@ -147,7 +203,7 @@ if [ -z "$1" ]; then echo -e "${CGREEN}#############################################${CEND}" echo -e ' wo stack upgrade ' echo -e "${CGREEN}#############################################${CEND}" - stack_upgrade='nginx php php73 mysql redis netdata dashboard phpmyadmin composer ngxblocker' + stack_upgrade='nginx php php72 php73 php74 mysql redis netdata dashboard phpmyadmin composer ngxblocker mysqltuner' for stack in $stack_upgrade; do echo -ne " Upgrading $stack [..]\r" if { @@ -232,7 +288,7 @@ echo -e "${CGREEN}#############################################${CEND}" echo -e ' wo site info ' echo -e "${CGREEN}#############################################${CEND}" -wo site info wp.net +wo site info wpfc.net echo echo -e "${CGREEN}#############################################${CEND}" @@ -240,10 +296,29 @@ echo -e ' wo info ' echo -e "${CGREEN}#############################################${CEND}" wo info +echo -e "${CGREEN}#############################################${CEND}" +echo -e ' wo site delete ' +echo -e "${CGREEN}#############################################${CEND}" +sites=$(wo site list 2>&1) +for site in $sites; do + echo -ne " deleting $site [..]\r" + if { + wo site delete $site --force + } >>/var/log/wo/test.log; then + echo -ne " deleting $site [${CGREEN}OK${CEND}]\\r" + echo -ne '\n' + else + echo -e " deleting $site [${CRED}FAIL${CEND}]" + echo -ne '\n' + exit_script + + fi +done + echo -e "${CGREEN}#############################################${CEND}" echo -e ' wo stack purge ' echo -e "${CGREEN}#############################################${CEND}" -stack_purge='nginx php php73 mysql redis fail2ban clamav proftpd netdata phpmyadmin composer dashboard extplorer adminer redis ufw ngxblocker cheat nanorc' +stack_purge='nginx php php73 php74 mysql redis fail2ban clamav proftpd netdata phpmyadmin composer dashboard extplorer adminer redis ufw ngxblocker cheat nanorc' for stack in $stack_purge; do echo -ne " purging $stack [..]\r" if { diff --git a/wo/cli/main.py b/wo/cli/main.py index 448aec5..f85c6b8 100644 --- a/wo/cli/main.py +++ b/wo/cli/main.py @@ -9,14 +9,6 @@ from cement.utils.misc import init_defaults from wo.core import exc -# this has to happen after you import sys, but before you import anything -# from Cement "source: https://github.com/datafolklabs/cement/issues/290" -if '--debug' in sys.argv: - sys.argv.remove('--debug') - TOGGLE_DEBUG = True -else: - TOGGLE_DEBUG = False - # Application default. Should update config/wo.conf to reflect any # changes, or additions here. defaults = init_defaults('wo') @@ -65,7 +57,7 @@ class WOApp(CementApp): # Internal templates (ship with application code) template_module = 'wo.cli.templates' - extensions = ['mustache'] + extensions = ['mustache', 'argcomplete', 'colorlog'] hooks = [ ("post_render", encode_output) @@ -73,10 +65,9 @@ class WOApp(CementApp): output_handler = 'mustache' + log_handler = 'colorlog' + arg_handler = WOArgHandler - - debug = TOGGLE_DEBUG - exit_on_close = True @@ -125,14 +116,11 @@ def main(): print('FrameworkError > %s' % e) app.exit_code = 1 finally: - # Print an exception (if it occurred) and --debug was passed + # Maybe we want to see a full-stack trace for the above + # exceptions, but only if --debug was passed? if app.debug: - import sys import traceback - - exc_type, exc_value, exc_traceback = sys.exc_info() - if exc_traceback is not None: - traceback.print_exc() + traceback.print_exc() if __name__ == '__main__': diff --git a/wo/cli/plugins/info.py b/wo/cli/plugins/info.py index 602a6ce..3c690e9 100644 --- a/wo/cli/plugins/info.py +++ b/wo/cli/plugins/info.py @@ -32,6 +32,9 @@ class WOInfoController(CementBaseController): (['--php73'], dict(help='Get PHP 7.3 configuration information', action='store_true')), + (['--php74'], + dict(help='Get PHP 7.4 configuration information', + action='store_true')), (['--nginx'], dict(help='Get Nginx configuration information', action='store_true')), @@ -238,6 +241,93 @@ class WOInfoController(CementBaseController): debug_xdebug_profiler_enable_trigger=debug_xdebug) self.app.render((data), 'info_php.mustache') + @expose(hide=True) + def info_php74(self): + """Display PHP information""" + version = os.popen("/usr/bin/php7.4 -v 2>/dev/null | " + "head -n1 | cut -d' ' -f2 |" + " cut -d'+' -f1 | tr -d '\n'").read + config = configparser.ConfigParser() + config.read('/etc/php/7.4/fpm/php.ini') + expose_php = config['PHP']['expose_php'] + memory_limit = config['PHP']['memory_limit'] + post_max_size = config['PHP']['post_max_size'] + upload_max_filesize = config['PHP']['upload_max_filesize'] + max_execution_time = config['PHP']['max_execution_time'] + + if os.path.exists('/etc/php/7.4/fpm/pool.d/www.conf'): + config.read('/etc/php/7.4/fpm/pool.d/www.conf') + else: + Log.error(self, 'php-fpm pool config not found') + if config.has_section('www'): + wconfig = config['www'] + elif config.has_section('www-php74'): + wconfig = config['www-php74'] + else: + Log.error(self, 'Unable to parse configuration') + www_listen = wconfig['listen'] + www_ping_path = wconfig['ping.path'] + www_pm_status_path = wconfig['pm.status_path'] + www_pm = wconfig['pm'] + www_pm_max_requests = wconfig['pm.max_requests'] + www_pm_max_children = wconfig['pm.max_children'] + www_pm_start_servers = wconfig['pm.start_servers'] + www_pm_min_spare_servers = wconfig['pm.min_spare_servers'] + www_pm_max_spare_servers = wconfig['pm.max_spare_servers'] + www_request_terminate_time = (wconfig + ['request_terminate_timeout']) + try: + www_xdebug = (wconfig + ['php_admin_flag[xdebug.profiler_enable' + '_trigger]']) + except Exception as e: + Log.debug(self, "{0}".format(e)) + www_xdebug = 'off' + + config.read('/etc/php/7.4/fpm/pool.d/debug.conf') + debug_listen = config['debug']['listen'] + debug_ping_path = config['debug']['ping.path'] + debug_pm_status_path = config['debug']['pm.status_path'] + debug_pm = config['debug']['pm'] + debug_pm_max_requests = config['debug']['pm.max_requests'] + debug_pm_max_children = config['debug']['pm.max_children'] + debug_pm_start_servers = config['debug']['pm.start_servers'] + debug_pm_min_spare_servers = config['debug']['pm.min_spare_servers'] + debug_pm_max_spare_servers = config['debug']['pm.max_spare_servers'] + debug_request_terminate = (config['debug'] + ['request_terminate_timeout']) + try: + debug_xdebug = (config['debug']['php_admin_flag[xdebug.profiler_' + 'enable_trigger]']) + except Exception as e: + Log.debug(self, "{0}".format(e)) + debug_xdebug = 'off' + + data = dict(version=version, expose_php=expose_php, + memory_limit=memory_limit, post_max_size=post_max_size, + upload_max_filesize=upload_max_filesize, + max_execution_time=max_execution_time, + www_listen=www_listen, www_ping_path=www_ping_path, + www_pm_status_path=www_pm_status_path, www_pm=www_pm, + www_pm_max_requests=www_pm_max_requests, + www_pm_max_children=www_pm_max_children, + www_pm_start_servers=www_pm_start_servers, + www_pm_min_spare_servers=www_pm_min_spare_servers, + www_pm_max_spare_servers=www_pm_max_spare_servers, + www_request_terminate_timeout=www_request_terminate_time, + www_xdebug_profiler_enable_trigger=www_xdebug, + debug_listen=debug_listen, debug_ping_path=debug_ping_path, + debug_pm_status_path=debug_pm_status_path, + debug_pm=debug_pm, + debug_pm_max_requests=debug_pm_max_requests, + debug_pm_max_children=debug_pm_max_children, + debug_pm_start_servers=debug_pm_start_servers, + debug_pm_min_spare_servers=debug_pm_min_spare_servers, + debug_pm_max_spare_servers=debug_pm_max_spare_servers, + debug_request_terminate_timeout=debug_request_terminate, + debug_xdebug_profiler_enable_trigger=debug_xdebug) + self.app.render((data), 'info_php.mustache') + @expose(hide=True) def info_mysql(self): """Display MySQL information""" @@ -275,38 +365,48 @@ class WOInfoController(CementBaseController): @expose(hide=True) def default(self): """default function for info""" - if (not self.app.pargs.nginx and not self.app.pargs.php and - not self.app.pargs.mysql and not self.app.pargs.php73): - self.app.pargs.nginx = True - self.app.pargs.php = True - self.app.pargs.mysql = True + pargs = self.app.pargs + if (not pargs.nginx and not pargs.php and + not pargs.mysql and not pargs.php73 and + not pargs.php74): + pargs.nginx = True + pargs.php = True + pargs.mysql = True if WOAptGet.is_installed(self, 'php7.3-fpm'): - self.app.pargs.php73 = True + pargs.php73 = True + if WOAptGet.is_installed(self, 'php7.4-fpm'): + pargs.php74 = True - if self.app.pargs.nginx: + if pargs.nginx: if ((not WOAptGet.is_installed(self, 'nginx-custom')) and (not os.path.exists('/usr/bin/nginx'))): - Log.error(self, "Nginx is not installed") + Log.info(self, "Nginx is not installed") else: self.info_nginx() - if self.app.pargs.php: + if pargs.php: if WOAptGet.is_installed(self, 'php7.2-fpm'): self.info_php() else: - Log.error(self, "PHP 7.2 is not installed") + Log.info(self, "PHP 7.2 is not installed") - if self.app.pargs.php73: + if pargs.php73: if WOAptGet.is_installed(self, 'php7.3-fpm'): self.info_php73() else: - Log.error(self, "PHP 7.3 is not installed") + Log.info(self, "PHP 7.3 is not installed") - if self.app.pargs.mysql: + if pargs.php74: + if WOAptGet.is_installed(self, 'php7.4-fpm'): + self.info_php74() + else: + Log.info(self, "PHP 7.4 is not installed") + + if pargs.mysql: if WOShellExec.cmd_exec(self, "/usr/bin/mysqladmin ping"): self.info_mysql() else: - Log.error(self, "MySQL is not installed") + Log.info(self, "MySQL is not installed") def load(app): diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index cf49c15..0d63995 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -1,5 +1,4 @@ import glob -import json import os import subprocess @@ -7,14 +6,14 @@ from cement.core.controller import CementBaseController, expose from wo.cli.plugins.site_functions import * from wo.cli.plugins.sitedb import (addNewSite, deleteSiteInfo, getAllsites, getSiteInfo, updateSiteInfo) -from wo.core.acme import WOAcme +from wo.cli.plugins.site_create import WOSiteCreateController +from wo.cli.plugins.site_update import WOSiteUpdateController from wo.core.domainvalidate import WODomain from wo.core.fileutils import WOFileUtils from wo.core.git import WOGit from wo.core.logging import Log -from wo.core.nginxhashbucket import hashbucket from wo.core.services import WOService -from wo.core.shellexec import WOShellExec +from wo.core.shellexec import WOShellExec, CommandExecutionError from wo.core.sslutils import SSL from wo.core.variables import WOVar @@ -56,7 +55,6 @@ class WOSiteController(CementBaseController): pargs.site_name = pargs.site_name.strip() # validate domain name wo_domain = WODomain.validate(self, pargs.site_name) - wo_www_domain = "www.{0}".format(wo_domain) # check if site exists if not check_domain_exists(self, wo_domain): @@ -94,7 +92,6 @@ class WOSiteController(CementBaseController): Log.error(self, 'could not input site name') pargs.site_name = pargs.site_name.strip() wo_domain = WODomain.validate(self, pargs.site_name) - wo_www_domain = "www.{0}".format(wo_domain) # check if site exists if not check_domain_exists(self, wo_domain): Log.error(self, "site {0} does not exist".format(wo_domain)) @@ -162,12 +159,7 @@ class WOSiteController(CementBaseController): ssl = ("enabled" if siteinfo.is_ssl else "disabled") if (ssl == "enabled"): sslprovider = "Lets Encrypt" - if os.path.islink("{0}/conf/nginx/ssl.conf" - .format(wo_site_webroot)): - sslexpiry = str( - SSL.getexpirationdays(self, wo_root_domain)) - else: - sslexpiry = str(SSL.getexpirationdays(self, wo_domain)) + sslexpiry = str(SSL.getexpirationdays(self, wo_domain)) else: sslprovider = '' sslexpiry = '' @@ -189,7 +181,6 @@ class WOSiteController(CementBaseController): pargs = self.app.pargs pargs.site_name = pargs.site_name.strip() wo_domain = WODomain.validate(self, pargs.site_name) - wo_www_domain = "www.{0}".format(wo_domain) wo_site_webroot = getSiteInfo(self, wo_domain).site_path if not check_domain_exists(self, wo_domain): @@ -243,18 +234,14 @@ class WOSiteController(CementBaseController): pargs.site_name = pargs.site_name.strip() wo_domain = WODomain.validate(self, pargs.site_name) - wo_www_domain = "www.{0}".format(wo_domain) if not check_domain_exists(self, wo_domain): Log.error(self, "site {0} does not exist".format(wo_domain)) wo_site_webroot = getSiteInfo(self, wo_domain).site_path - WOFileUtils.chdir(self, wo_site_webroot) - - try: - subprocess.call(['/bin/bash']) - except OSError as e: - Log.debug(self, "{0}{1}".format(e.errno, e.strerror)) + if os.path.isdir(wo_site_webroot): + WOFileUtils.chdir(self, wo_site_webroot) + else: Log.error(self, "unable to change directory") @@ -311,1606 +298,6 @@ class WOSiteEditController(CementBaseController): Log.error(self, "nginx configuration file does not exists") -class WOSiteCreateController(CementBaseController): - class Meta: - label = 'create' - stacked_on = 'site' - stacked_type = 'nested' - description = ('this commands set up configuration and installs ' - 'required files as options are provided') - arguments = [ - (['site_name'], - dict(help='domain name for the site to be created.', - nargs='?')), - (['--html'], - dict(help="create html site", action='store_true')), - (['--php'], - dict(help="create php 7.2 site", action='store_true')), - (['--php72'], - dict(help="create php 7.2 site", action='store_true')), - (['--php73'], - dict(help="create php 7.3 site", action='store_true')), - (['--mysql'], - dict(help="create mysql site", action='store_true')), - (['--wp'], - dict(help="create WordPress single site", - action='store_true')), - (['--wpsubdir'], - dict(help="create WordPress multisite with subdirectory setup", - action='store_true')), - (['--wpsubdomain'], - dict(help="create WordPress multisite with subdomain setup", - action='store_true')), - (['--wpfc'], - dict(help="create WordPress single/multi site with " - "Nginx fastcgi_cache", - action='store_true')), - (['--wpsc'], - dict(help="create WordPress single/multi site with wpsc cache", - action='store_true')), - (['--wprocket'], - dict(help="create WordPress single/multi site with WP-Rocket", - action='store_true')), - (['--wpce'], - dict(help="create WordPress single/multi site with Cache-Enabler", - action='store_true')), - (['--wpredis'], - dict(help="create WordPress single/multi site " - "with redis cache", - action='store_true')), - (['-le', '--letsencrypt'], - dict(help="configure letsencrypt ssl for the site", - action='store' or 'store_const', - choices=('on', 'subdomain', 'wildcard'), - const='on', nargs='?')), - (['--force'], - dict(help="force Let's Encrypt certificate issuance", - action='store_true')), - (['--dns'], - dict(help="choose dns provider api for letsencrypt", - action='store' or 'store_const', - const='dns_cf', nargs='?')), - (['--dnsalias'], - dict(help="set domain used for acme dns alias validation", - action='store', nargs='?')), - (['--hsts'], - dict(help="enable HSTS for site secured with letsencrypt", - action='store_true')), - (['--ngxblocker'], - dict(help="enable HSTS for site secured with letsencrypt", - action='store_true')), - (['--user'], - dict(help="provide user for WordPress site")), - (['--email'], - dict(help="provide email address for WordPress site")), - (['--pass'], - dict(help="provide password for WordPress user", - dest='wppass')), - (['--proxy'], - dict(help="create proxy for site", nargs='+')), - (['--vhostonly'], dict(help="only create vhost and database " - "without installing WordPress", - action='store_true')), - ] - - @expose(hide=True) - def default(self): - pargs = self.app.pargs - if pargs.php72: - pargs.php = True - # self.app.render((data), 'default.mustache') - # Check domain name validation - data = dict() - host, port = None, None - try: - stype, cache = detSitePar(vars(pargs)) - except RuntimeError as e: - Log.debug(self, str(e)) - Log.error(self, "Please provide valid options to creating site") - - if stype is None and pargs.proxy: - stype, cache = 'proxy', '' - proxyinfo = pargs.proxy[0].strip() - if not proxyinfo: - Log.error(self, "Please provide proxy server host information") - proxyinfo = proxyinfo.split(':') - host = proxyinfo[0].strip() - port = '80' if len(proxyinfo) < 2 else proxyinfo[1].strip() - elif stype is None and not pargs.proxy: - stype, cache = 'html', 'basic' - elif stype and pargs.proxy: - Log.error(self, "proxy should not be used with other site types") - - if not pargs.site_name: - try: - while not pargs.site_name: - # preprocessing before finalize site name - pargs.site_name = (input('Enter site name : ') - .strip()) - except IOError as e: - Log.debug(self, str(e)) - Log.error(self, "Unable to input site name, Please try again!") - - pargs.site_name = pargs.site_name.strip() - wo_domain = WODomain.validate(self, pargs.site_name) - wo_www_domain = "www.{0}".format(wo_domain) - (wo_domain_type, wo_root_domain) = WODomain.getlevel( - self, wo_domain) - if not wo_domain.strip(): - Log.error(self, "Invalid domain name, " - "Provide valid domain name") - - wo_site_webroot = WOVar.wo_webroot + wo_domain - - if check_domain_exists(self, wo_domain): - Log.error(self, "site {0} already exists".format(wo_domain)) - elif os.path.isfile('/etc/nginx/sites-available/{0}' - .format(wo_domain)): - Log.error(self, "Nginx configuration /etc/nginx/sites-available/" - "{0} already exists".format(wo_domain)) - - if stype == 'proxy': - data = dict(site_name=wo_domain, www_domain=wo_www_domain, - static=True, basic=False, php73=False, wp=False, - wpfc=False, wpsc=False, wprocket=False, wpce=False, - multisite=False, - wpsubdir=False, webroot=wo_site_webroot) - data['proxy'] = True - data['host'] = host - data['port'] = port - data['basic'] = True - - if pargs.php73: - data = dict(site_name=wo_domain, www_domain=wo_www_domain, - static=False, basic=False, php73=True, wp=False, - wpfc=False, wpsc=False, wprocket=False, wpce=False, - multisite=False, - wpsubdir=False, webroot=wo_site_webroot) - data['basic'] = True - - if stype in ['html', 'php']: - data = dict(site_name=wo_domain, www_domain=wo_www_domain, - static=True, basic=False, php73=False, wp=False, - wpfc=False, wpsc=False, wprocket=False, wpce=False, - multisite=False, - wpsubdir=False, webroot=wo_site_webroot) - - if stype == 'php': - data['static'] = False - data['basic'] = True - - elif stype in ['mysql', 'wp', 'wpsubdir', 'wpsubdomain']: - - data = dict(site_name=wo_domain, www_domain=wo_www_domain, - static=False, basic=True, wp=False, wpfc=False, - wpsc=False, wpredis=False, wprocket=False, wpce=False, - multisite=False, - wpsubdir=False, webroot=wo_site_webroot, - wo_db_name='', wo_db_user='', wo_db_pass='', - wo_db_host='') - - if stype in ['wp', 'wpsubdir', 'wpsubdomain']: - data['wp'] = True - data['basic'] = False - data[cache] = True - data['wp-user'] = pargs.user - data['wp-email'] = pargs.email - data['wp-pass'] = pargs.wppass - if stype in ['wpsubdir', 'wpsubdomain']: - data['multisite'] = True - if stype == 'wpsubdir': - data['wpsubdir'] = True - else: - pass - - if data and pargs.php73: - data['php73'] = True - elif data: - data['php73'] = False - - if ((not pargs.wpfc) and (not pargs.wpsc) and - (not pargs.wprocket) and - (not pargs.wpce) and - (not pargs.wpredis)): - data['basic'] = True - - if (cache == 'wpredis'): - cache = 'wpredis' - data['wpredis'] = True - data['basic'] = False - pargs.wpredis = True - - # Check rerequired packages are installed or not - wo_auth = site_package_check(self, stype) - - try: - pre_run_checks(self) - except SiteError as e: - Log.debug(self, str(e)) - Log.error(self, "NGINX configuration check failed.") - - try: - try: - # setup NGINX configuration, and webroot - setupdomain(self, data) - - # Fix Nginx Hashbucket size error - hashbucket(self) - except SiteError as e: - # call cleanup actions on failure - Log.info(self, Log.FAIL + - "There was a serious error encountered...") - Log.info(self, Log.FAIL + "Cleaning up afterwards...") - doCleanupAction(self, domain=wo_domain, - webroot=data['webroot']) - Log.debug(self, str(e)) - Log.error(self, "Check the log for details: " - "`tail /var/log/wo/wordops.log` " - "and please try again") - - if 'proxy' in data.keys() and data['proxy']: - addNewSite(self, wo_domain, stype, cache, wo_site_webroot) - # Service Nginx Reload - if not WOService.reload_service(self, 'nginx'): - Log.info(self, Log.FAIL + - "There was a serious error encountered...") - Log.info(self, Log.FAIL + "Cleaning up afterwards...") - doCleanupAction(self, domain=wo_domain) - deleteSiteInfo(self, wo_domain) - Log.error(self, "service nginx reload failed. " - "check issues with `nginx -t` command") - Log.error(self, "Check the log for details: " - "`tail /var/log/wo/wordops.log` " - "and please try again") - if wo_auth and len(wo_auth): - for msg in wo_auth: - Log.info(self, Log.ENDC + msg, log=False) - Log.info(self, "Successfully created site" - " http://{0}".format(wo_domain)) - return - - if data['php73']: - php_version = "7.3" - php73 = 1 - else: - php_version = "7.2" - php73 = 0 - - addNewSite(self, wo_domain, stype, cache, wo_site_webroot, - php_version=php_version) - - # Setup database for MySQL site - if 'wo_db_name' in data.keys() and not data['wp']: - try: - data = setupdatabase(self, data) - # Add database information for site into database - updateSiteInfo(self, wo_domain, db_name=data['wo_db_name'], - db_user=data['wo_db_user'], - db_password=data['wo_db_pass'], - db_host=data['wo_db_host']) - except SiteError as e: - # call cleanup actions on failure - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + - "There was a serious error encountered...") - Log.info(self, Log.FAIL + "Cleaning up afterwards...") - doCleanupAction(self, domain=wo_domain, - webroot=data['webroot'], - dbname=data['wo_db_name'], - dbuser=data['wo_db_user'], - dbhost=data['wo_db_host']) - deleteSiteInfo(self, wo_domain) - Log.error(self, "Check the log for details: " - "`tail /var/log/wo/wordops.log` " - "and please try again") - - try: - wodbconfig = open("{0}/wo-config.php" - .format(wo_site_webroot), - encoding='utf-8', mode='w') - wodbconfig.write("" - .format(data['wo_db_name'], - data['wo_db_user'], - data['wo_db_pass'], - data['wo_db_host'])) - wodbconfig.close() - stype = 'mysql' - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "wo-config.php") - Log.info(self, Log.FAIL + - "There was a serious error encountered...") - Log.info(self, Log.FAIL + "Cleaning up afterwards...") - doCleanupAction(self, domain=wo_domain, - webroot=data['webroot'], - dbname=data['wo_db_name'], - dbuser=data['wo_db_user'], - dbhost=data['wo_db_host']) - deleteSiteInfo(self, wo_domain) - Log.error(self, "Check the log for details: " - "`tail /var/log/wo/wordops.log` " - "and please try again") - - # Setup WordPress if Wordpress site - if data['wp']: - vhostonly = bool(pargs.vhostonly) - try: - wo_wp_creds = setupwordpress(self, data, vhostonly) - # Add database information for site into database - updateSiteInfo(self, wo_domain, - db_name=data['wo_db_name'], - db_user=data['wo_db_user'], - db_password=data['wo_db_pass'], - db_host=data['wo_db_host']) - except SiteError as e: - # call cleanup actions on failure - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + - "There was a serious error encountered...") - Log.info(self, Log.FAIL + "Cleaning up afterwards...") - doCleanupAction(self, domain=wo_domain, - webroot=data['webroot'], - dbname=data['wo_db_name'], - dbuser=data['wo_db_user'], - dbhost=data['wo_mysql_grant_host']) - deleteSiteInfo(self, wo_domain) - Log.error(self, "Check the log for details: " - "`tail /var/log/wo/wordops.log` " - "and please try again") - - # Service Nginx Reload call cleanup if failed to reload nginx - if not WOService.reload_service(self, 'nginx'): - Log.info(self, Log.FAIL + - "There was a serious error encountered...") - Log.info(self, Log.FAIL + "Cleaning up afterwards...") - doCleanupAction(self, domain=wo_domain, - webroot=data['webroot']) - if 'wo_db_name' in data.keys(): - doCleanupAction(self, domain=wo_domain, - dbname=data['wo_db_name'], - dbuser=data['wo_db_user'], - dbhost=data['wo_mysql_grant_host']) - deleteSiteInfo(self, wo_domain) - Log.info(self, Log.FAIL + "service nginx reload failed." - " check issues with `nginx -t` command.") - Log.error(self, "Check the log for details: " - "`tail /var/log/wo/wordops.log` " - "and please try again") - - WOGit.add(self, ["/etc/nginx"], - msg="{0} created with {1} {2}" - .format(wo_www_domain, stype, cache)) - # Setup Permissions for webroot - try: - setwebrootpermissions(self, data['webroot']) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + - "There was a serious error encountered...") - Log.info(self, Log.FAIL + "Cleaning up afterwards...") - doCleanupAction(self, domain=wo_domain, - webroot=data['webroot']) - if 'wo_db_name' in data.keys(): - print("Inside db cleanup") - doCleanupAction(self, domain=wo_domain, - dbname=data['wo_db_name'], - dbuser=data['wo_db_user'], - dbhost=data['wo_mysql_grant_host']) - deleteSiteInfo(self, wo_domain) - Log.error(self, "Check the log for details: " - "`tail /var/log/wo/wordops.log` and " - "please try again") - - if wo_auth and len(wo_auth): - for msg in wo_auth: - Log.info(self, Log.ENDC + msg, log=False) - - if data['wp'] and (not pargs.vhostonly): - Log.info(self, Log.ENDC + "WordPress admin user :" - " {0}".format(wo_wp_creds['wp_user']), log=False) - Log.info(self, Log.ENDC + "WordPress admin password : {0}" - .format(wo_wp_creds['wp_pass']), log=False) - - display_cache_settings(self, data) - - Log.info(self, "Successfully created site" - " http://{0}".format(wo_domain)) - except SiteError: - Log.error(self, "Check the log for details: " - "`tail /var/log/wo/wordops.log` and please try again") - - if pargs.letsencrypt: - acme_domains = [] - data['letsencrypt'] = True - letsencrypt = True - if WOAcme.cert_check(self, wo_domain): - archivedCertificateHandle(self, wo_domain) - else: - Log.debug(self, "Going to issue Let's Encrypt certificate") - acmedata = dict(acme_domains, dns=False, acme_dns='dns_cf', - dnsalias=False, acme_alias='', keylength='') - if self.app.config.has_section('letsencrypt'): - acmedata['keylength'] = self.app.config.get( - 'letsencrypt', 'keylength') - else: - acmedata['keylength'] = 'ec-384' - if pargs.dns: - Log.debug(self, "DNS validation enabled") - acmedata['dns'] = True - if not pargs.dns == 'dns_cf': - Log.debug(self, "DNS API : {0}".format(pargs.dns)) - acmedata['acme_dns'] = pargs.dns - if pargs.dnsalias: - Log.debug(self, "DNS Alias enabled") - acmedata['dnsalias'] = True - acmedata['acme_alias'] = pargs.dnsalias - - # detect subdomain and set subdomain variable - if pargs.letsencrypt == "subdomain": - Log.warn( - self, 'Flag --letsencrypt=subdomain is ' - 'deprecated and not required anymore.') - acme_subdomain = True - acme_wildcard = False - elif pargs.letsencrypt == "wildcard": - acme_wildcard = True - acme_subdomain = False - acmedata['dns'] = True - else: - if ((wo_domain_type == 'subdomain')): - Log.debug(self, "Domain type = {0}" - .format(wo_domain_type)) - acme_subdomain = True - else: - acme_subdomain = False - acme_wildcard = False - - if acme_subdomain is True: - Log.info(self, "Certificate type : subdomain") - acme_domains = acme_domains + ['{0}'.format(wo_domain)] - elif acme_wildcard is True: - Log.info(self, "Certificate type : wildcard") - acme_domains = acme_domains + ['{0}'.format(wo_domain), - '*.{0}'.format(wo_domain)] - else: - Log.info(self, "Certificate type : domain") - acme_domains = acme_domains + ['{0}'.format(wo_domain), - 'www.{0}'.format(wo_domain)] - - if acme_subdomain is True: - # check if a wildcard cert for the root domain exist - Log.debug(self, "checkWildcardExist on *.{0}" - .format(wo_root_domain)) - if SSL.checkwildcardexist(self, wo_root_domain): - Log.info(self, "Using existing Wildcard SSL " - "certificate from {0} to secure {1}" - .format(wo_root_domain, wo_domain)) - Log.debug(self, "symlink wildcard " - "cert between {0} & {1}" - .format(wo_domain, wo_root_domain)) - # copy the cert from the root domain - copyWildcardCert(self, wo_domain, wo_root_domain) - else: - # check DNS records before issuing cert - if not acmedata['dns'] is True: - if not pargs.force: - if not WOAcme.check_dns(self, acme_domains): - Log.error(self, - "Aborting SSL " - "certificate issuance") - Log.debug(self, "Setup Cert with acme.sh for {0}" - .format(wo_domain)) - if WOAcme.setupletsencrypt( - self, acme_domains, acmedata): - WOAcme.deploycert(self, wo_domain) - else: - if not acmedata['dns'] is True: - if not pargs.force: - if not WOAcme.check_dns(self, acme_domains): - Log.error(self, - "Aborting SSL certificate issuance") - if WOAcme.setupletsencrypt( - self, acme_domains, acmedata): - WOAcme.deploycert(self, wo_domain) - - if pargs.hsts: - SSL.setuphsts(self, wo_domain) - - httpsRedirect(self, wo_domain, True, acme_wildcard) - SSL.siteurlhttps(self, wo_domain) - if not WOService.reload_service(self, 'nginx'): - Log.error(self, "service nginx reload failed. " - "check issues with `nginx -t` command") - Log.info(self, "Congratulations! Successfully Configured " - "SSl for Site " - " https://{0}".format(wo_domain)) - - # Add nginx conf folder into GIT - WOGit.add(self, ["{0}/conf/nginx".format(wo_site_webroot)], - msg="Adding letsencrypts config of site: {0}" - .format(wo_domain)) - updateSiteInfo(self, wo_domain, ssl=letsencrypt) - - -class WOSiteUpdateController(CementBaseController): - class Meta: - label = 'update' - stacked_on = 'site' - stacked_type = 'nested' - description = ('This command updates websites configuration to ' - 'another as per the options are provided') - arguments = [ - (['site_name'], - dict(help='domain name for the site to be updated', - nargs='?')), - (['--password'], - dict(help="update to password for wordpress site user", - action='store_true')), - (['--html'], - dict(help="update to html site", action='store_true')), - (['--php72'], - dict(help="update to php site", action='store_true')), - (['--php'], - dict(help="update to php site", action='store_true')), - (['--php73'], - dict(help="update to php73 site", - action='store' or 'store_const', - choices=('on', 'off'), const='on', nargs='?')), - (['--mysql'], - dict(help="update to mysql site", action='store_true')), - (['--wp'], - dict(help="update to wordpress single site", - action='store_true')), - (['--wpsubdir'], - dict(help="update to wpsubdir site", action='store_true')), - (['--wpsubdomain'], - dict(help="update to wpsubdomain site", action='store_true')), - (['--wpfc'], - dict(help="update to wpfc cache", action='store_true')), - (['--wpsc'], - dict(help="update to wpsc cache", action='store_true')), - (['--wprocket'], - dict(help="update to WP-Rocket cache", action='store_true')), - (['--wpce'], - dict(help="update to Cache-Enabler cache", - action='store_true')), - (['--wpredis'], - dict(help="update to redis cache", action='store_true')), - (['-le', '--letsencrypt'], - dict(help="configure letsencrypt ssl for the site", - action='store' or 'store_const', - choices=('on', 'off', 'renew', 'subdomain', - 'wildcard', 'clean', 'purge'), - const='on', nargs='?')), - (['--force'], - dict(help="force LetsEncrypt certificate issuance/renewal", - action='store_true')), - (['--dns'], - dict(help="choose dns provider api for letsencrypt", - action='store' or 'store_const', - const='dns_cf', nargs='?')), - (['--dnsalias'], - dict(help="set domain used for acme dns alias validation", - action='store', nargs='?')), - (['--hsts'], - dict(help="configure hsts for the site", - action='store' or 'store_const', - choices=('on', 'off'), - const='on', nargs='?')), - (['--ngxblocker'], - dict(help="enable Ultimate Nginx bad bot blocker", - action='store' or 'store_const', - choices=('on', 'off'), - const='on', nargs='?')), - (['--proxy'], - dict(help="update to proxy site", nargs='+')), - (['--all'], - dict(help="update all sites", action='store_true')), - ] - - @expose(help="Update site type or cache") - def default(self): - pargs = self.app.pargs - - if pargs.php72: - pargs.php = True - - if pargs.all: - if pargs.site_name: - Log.error(self, "`--all` option cannot be used with site name" - " provided") - if pargs.html: - Log.error(self, "No site can be updated to html") - - if not (pargs.php or pargs.php73 or - pargs.mysql or pargs.wp or pargs.wpsubdir or - pargs.wpsubdomain or pargs.wpfc or pargs.wpsc or - pargs.wprocket or pargs.wpce or - pargs.wpredis or pargs.letsencrypt or pargs.hsts or - pargs.dns or pargs.force): - Log.error(self, "Please provide options to update sites.") - - if pargs.all: - if pargs.site_name: - Log.error(self, "`--all` option cannot be used with site name" - " provided") - - sites = getAllsites(self) - if not sites: - pass - else: - for site in sites: - pargs.site_name = site.sitename - Log.info(self, Log.ENDC + Log.BOLD + "Updating site {0}," - " please wait..." - .format(pargs.site_name)) - self.doupdatesite(pargs) - print("\n") - else: - self.doupdatesite(pargs) - - def doupdatesite(self, pargs): - pargs = self.app.pargs - letsencrypt = False - php73 = None - - data = dict() - try: - stype, cache = detSitePar(vars(pargs)) - except RuntimeError as e: - Log.debug(self, str(e)) - Log.error(self, "Please provide valid options combination for" - " site update") - - if stype is None and pargs.proxy: - stype, cache = 'proxy', '' - proxyinfo = pargs.proxy[0].strip() - if not proxyinfo: - Log.error(self, "Please provide proxy server host information") - proxyinfo = proxyinfo.split(':') - host = proxyinfo[0].strip() - port = '80' if len(proxyinfo) < 2 else proxyinfo[1].strip() - elif stype is None and not (pargs.proxy or pargs.letsencrypt): - stype, cache = 'html', 'basic' - elif stype and pargs.proxy: - Log.error(self, "--proxy can not be used with other site types") - - if not pargs.site_name: - try: - while not pargs.site_name: - pargs.site_name = (input('Enter site name : ').strip()) - except IOError: - Log.error(self, 'Unable to input site name, Please try again!') - - pargs.site_name = pargs.site_name.strip() - wo_domain = WODomain.validate(self, pargs.site_name) - wo_www_domain = "www.{0}".format(wo_domain) - (wo_domain_type, wo_root_domain) = WODomain.getlevel( - self, wo_domain) - wo_site_webroot = WOVar.wo_webroot + wo_domain - check_site = getSiteInfo(self, wo_domain) - - if check_site is None: - Log.error(self, " Site {0} does not exist.".format(wo_domain)) - else: - oldsitetype = check_site.site_type - oldcachetype = check_site.cache_type - check_ssl = check_site.is_ssl - check_php_version = check_site.php_version - - old_php73 = bool(check_php_version == "7.3") - - if (pargs.password and not (pargs.html or - pargs.php or pargs.php73 or pargs.mysql or - pargs.wp or pargs.wpfc or pargs.wpsc or - pargs.wprocket or pargs.wpce or - pargs.wpsubdir or pargs.wpsubdomain or - pargs.hsts or pargs.ngxblocker)): - try: - updatewpuserpassword(self, wo_domain, wo_site_webroot) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, "\nPassword Unchanged.") - return 0 - - if (pargs.hsts and not (pargs.html or - pargs.php or pargs.php73 or pargs.mysql or - pargs.wp or pargs.wpfc or pargs.wpsc or - pargs.wprocket or pargs.wpce or - pargs.wpsubdir or pargs.wpsubdomain or - pargs.ngxblocker)): - if pargs.hsts == "on": - try: - SSL.setuphsts(self, wo_domain) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, "\nHSTS not enabled.") - elif pargs.hsts == "off": - if os.path.isfile( - '/var/www/{0}/conf/nginx/hsts.conf' - .format(wo_domain)): - WOFileUtils.mvfile(self, '/var/www/{0}/conf/' - 'nginx/hsts.conf' - .format(wo_domain), - '/var/www/{0}/conf/' - 'nginx/hsts.conf.disabled' - .format(wo_domain)) - else: - Log.error(self, "HSTS isn't enabled") - # Service Nginx Reload - if not WOService.reload_service(self, 'nginx'): - Log.error(self, "service nginx reload failed. " - "check issues with `nginx -t` command") - return 0 - - if (pargs.ngxblocker and not (pargs.html or - pargs.php or pargs.php73 or - pargs.mysql or - pargs.wp or pargs.wpfc or pargs.wpsc or - pargs.wprocket or pargs.wpce or - pargs.wpsubdir or pargs.wpsubdomain or - pargs.hsts)): - if pargs.ngxblocker == "on": - if os.path.isdir('/etc/nginx/bots.d'): - try: - setupngxblocker(self, wo_domain) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, "\nngxblocker not enabled.") - else: - Log.error(self, 'ngxblocker stack is not installed') - elif pargs.ngxblocker == "off": - try: - setupngxblocker(self, wo_domain, False) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, "\nngxblocker not enabled.") - - # Service Nginx Reload - if not WOService.reload_service(self, 'nginx'): - Log.error(self, "service nginx reload failed. " - "check issues with `nginx -t` command") - return 0 - # - if (pargs.letsencrypt == 'renew' and - not (pargs.html or - pargs.php or pargs.php73 or pargs.mysql or - pargs.wp or pargs.wpfc or pargs.wpsc or - pargs.wprocket or pargs.wpce or - pargs.wpsubdir or pargs.wpsubdomain or - pargs.ngxblocker or pargs.hsts)): - - if WOAcme.cert_check(self, wo_domain): - if not pargs.force: - if (SSL.getexpirationdays(self, wo_domain) > 30): - Log.error( - self, "Your cert will expire in more " - "than 30 days ( " + - str(SSL.getexpirationdays(self, wo_domain)) + - " days).\nAdd \'--force\' to force to renew") - Log.wait(self, "Renewing SSL certificate") - if WOAcme.renew(self, wo_domain): - Log.valide(self, "Renewing SSL certificate") - else: - Log.error(self, "Certificate doesn't exist") - return 0 - - if ((stype == 'php' and - oldsitetype not in ['html', 'proxy', 'php73']) or - (stype == 'mysql' and oldsitetype not in ['html', 'php', - 'proxy', 'php73']) or - (stype == 'wp' and oldsitetype not in ['html', 'php', 'mysql', - 'proxy', 'wp', 'php73']) or - (stype == 'wpsubdir' and oldsitetype in ['wpsubdomain']) or - (stype == 'wpsubdomain' and oldsitetype in ['wpsubdir']) or - (stype == oldsitetype and cache == oldcachetype) and not - pargs.php73): - Log.info(self, Log.FAIL + "can not update {0} {1} to {2} {3}". - format(oldsitetype, oldcachetype, stype, cache)) - return 1 - - if stype == 'proxy': - data['site_name'] = wo_domain - data['www_domain'] = wo_www_domain - data['proxy'] = True - data['host'] = host - data['port'] = port - data['webroot'] = wo_site_webroot - data['currsitetype'] = oldsitetype - data['currcachetype'] = oldcachetype - - if stype == 'php': - data = dict( - site_name=wo_domain, www_domain=wo_www_domain, - static=False, basic=True, wp=False, wpfc=False, - wpsc=False, wpredis=False, wprocket=False, wpce=False, - multisite=False, wpsubdir=False, webroot=wo_site_webroot, - currsitetype=oldsitetype, currcachetype=oldcachetype) - - elif stype in ['mysql', 'wp', 'wpsubdir', 'wpsubdomain']: - - data = dict( - site_name=wo_domain, www_domain=wo_www_domain, - static=False, basic=True, wp=False, wpfc=False, - wpsc=False, wpredis=False, wprocket=False, wpce=False, - multisite=False, wpsubdir=False, webroot=wo_site_webroot, - wo_db_name='', wo_db_user='', wo_db_pass='', - wo_db_host='', - currsitetype=oldsitetype, currcachetype=oldcachetype) - - if stype in ['wp', 'wpsubdir', 'wpsubdomain']: - data['wp'] = True - data['basic'] = False - data[cache] = True - if stype in ['wpsubdir', 'wpsubdomain']: - data['multisite'] = True - if stype == 'wpsubdir': - data['wpsubdir'] = True - - if pargs.php73: - if not data: - data = dict( - site_name=wo_domain, - www_domain=wo_www_domain, - currsitetype=oldsitetype, - currcachetype=oldcachetype, - webroot=wo_site_webroot) - stype = oldsitetype - cache = oldcachetype - if oldsitetype == 'html' or oldsitetype == 'proxy': - data['static'] = False - data['wp'] = False - data['multisite'] = False - data['wpsubdir'] = False - elif oldsitetype == 'php' or oldsitetype == 'mysql': - data['static'] = False - data['wp'] = False - data['multisite'] = False - data['wpsubdir'] = False - elif oldsitetype == 'wp': - data['static'] = False - data['wp'] = True - data['multisite'] = False - data['wpsubdir'] = False - elif oldsitetype == 'wpsubdir': - data['static'] = False - data['wp'] = True - data['multisite'] = True - data['wpsubdir'] = True - elif oldsitetype == 'wpsubdomain': - data['static'] = False - data['wp'] = True - data['multisite'] = True - data['wpsubdir'] = False - - if oldcachetype == 'basic': - data['basic'] = True - data['wpfc'] = False - data['wpsc'] = False - data['wpredis'] = False - data['wprocket'] = False - data['wpce'] = False - elif oldcachetype == 'wpfc': - data['basic'] = False - data['wpfc'] = True - data['wpsc'] = False - data['wpredis'] = False - data['wprocket'] = False - data['wpce'] = False - elif oldcachetype == 'wpsc': - data['basic'] = False - data['wpfc'] = False - data['wpsc'] = True - data['wpredis'] = False - data['wprocket'] = False - data['wpce'] = False - elif oldcachetype == 'wpredis': - data['basic'] = False - data['wpfc'] = False - data['wpsc'] = False - data['wpredis'] = True - data['wprocket'] = False - data['wpce'] = False - elif oldcachetype == 'wprocket': - data['basic'] = False - data['wpfc'] = False - data['wpsc'] = False - data['wpredis'] = False - data['wprocket'] = True - data['wpce'] = False - elif oldcachetype == 'wpce': - data['basic'] = False - data['wpfc'] = False - data['wpsc'] = False - data['wpredis'] = False - data['wprocket'] = False - data['wpce'] = True - - if pargs.php73 == 'on': - data['php73'] = True - php73 = True - check_php_version = '7.3' - elif pargs.php73 == 'off': - data['php73'] = False - php73 = False - check_php_version = '7.2' - - if pargs.php73: - if php73 is old_php73: - if php73 is False: - Log.info(self, "PHP 7.3 is already disabled for given " - "site") - elif php73 is True: - Log.info(self, "PHP 7.3 is already enabled for given " - "site") - pargs.php73 = False - - if pargs.letsencrypt: - acme_domains = [] - acmedata = dict(acme_domains, dns=False, acme_dns='dns_cf', - dnsalias=False, acme_alias='', keylength='') - acmedata['keylength'] = self.app.config.get('letsencrypt', - 'keylength') - if pargs.letsencrypt == 'on': - data['letsencrypt'] = True - letsencrypt = True - acme_subdomain = bool(wo_domain_type == 'subdomain') - acme_wildcard = False - elif pargs.letsencrypt == 'subdomain': - data['letsencrypt'] = True - letsencrypt = True - acme_subdomain = True - acme_wildcard = False - elif pargs.letsencrypt == 'wildcard': - data['letsencrypt'] = True - letsencrypt = True - acme_wildcard = True - acme_subdomain = False - acmedata['dns'] = True - elif pargs.letsencrypt == 'off': - data['letsencrypt'] = False - letsencrypt = False - acme_subdomain = False - acme_wildcard = False - elif pargs.letsencrypt == 'clean': - data['letsencrypt'] = False - letsencrypt = False - acme_subdomain = False - acme_wildcard = False - elif pargs.letsencrypt == 'purge': - data['letsencrypt'] = False - letsencrypt = False - acme_subdomain = False - acme_wildcard = False - else: - data['letsencrypt'] = False - letsencrypt = False - acme_subdomain = False - acme_wildcard = False - - if not (acme_subdomain is True): - if letsencrypt is check_ssl: - if letsencrypt is False: - Log.error(self, "SSL is not configured for given " - "site") - elif letsencrypt is True: - Log.error(self, "SSL is already configured for given " - "site") - pargs.letsencrypt = False - - if pargs.all and pargs.letsencrypt == "off": - if letsencrypt is check_ssl: - if letsencrypt is False: - Log.error(self, "HTTPS is not configured for given " - "site", False) - return 0 - - if data and (not pargs.php73): - data['php73'] = bool(old_php73 is True) - php73 = bool(old_php73 is True) - - data['php73'] = bool(pargs.php73 == "on") - php73 = bool(pargs.php73 == "on") - - if pargs.wpredis and data['currcachetype'] != 'wpredis': - data['wpredis'] = True - data['basic'] = False - cache = 'wpredis' - - if pargs.wprocket and data['currcachetype'] != 'wprocket': - data['wprocket'] = True - data['basic'] = False - cache = 'wprocket' - - if pargs.wpce and data['currcachetype'] != 'wpce': - data['wpce'] = True - data['basic'] = False - cache = 'wpce' - - if (php73 is old_php73) and (stype == oldsitetype and - cache == oldcachetype): - return 1 - - if pargs.hsts: - data['hsts'] = bool(pargs.hsts == "on") - - if pargs.ngxblocker: - ngxblocker = bool(pargs.ngxblocker == 'on') - - if not data: - Log.error(self, "Cannot update {0}, Invalid Options" - .format(wo_domain)) - - wo_auth = site_package_check(self, stype) - data['wo_db_name'] = check_site.db_name - data['wo_db_user'] = check_site.db_user - data['wo_db_pass'] = check_site.db_password - data['wo_db_host'] = check_site.db_host - - if not (pargs.letsencrypt or pargs.hsts or pargs.ngxblocker): - try: - pre_run_checks(self) - except SiteError as e: - Log.debug(self, str(e)) - Log.error(self, "NGINX configuration check failed.") - - try: - sitebackup(self, data) - except Exception as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Check the log for details: " - "`tail /var/log/wo/wordops.log` and please try again") - return 1 - - # setup NGINX configuration, and webroot - try: - setupdomain(self, data) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Update site failed." - "Check the log for details:" - "`tail /var/log/wo/wordops.log` and please try again") - return 1 - - if 'proxy' in data.keys() and data['proxy']: - updateSiteInfo(self, wo_domain, stype=stype, cache=cache, - ssl=(bool(check_site.is_ssl))) - Log.info(self, "Successfully updated site" - " http://{0}".format(wo_domain)) - return 0 - - if pargs.letsencrypt: - if data['letsencrypt'] is True: - if WOAcme.cert_check(self, wo_domain): - archivedCertificateHandle(self, wo_domain) - else: - # DNS API configuration - if pargs.dns: - Log.debug(self, "DNS validation enabled") - acmedata['dns'] = True - if not pargs.dns == 'dns_cf': - Log.debug(self, "DNS API : {0}".format(pargs.dns)) - acmedata['acme_dns'] = pargs.dns - if pargs.dnsalias: - Log.debug(self, "DNS Alias enabled") - acmedata['dnsalias'] = True - acmedata['acme_alias'] = pargs.dnsalias - # Set list of domains to secure - if acme_subdomain is True: - Log.info(self, "Certificate type : subdomain") - acme_domains = acme_domains + ['{0}'.format(wo_domain)] - elif acme_wildcard is True: - Log.info(self, "Certificate type : wildcard") - acme_domains = \ - acme_domains + ['{0}'.format(wo_domain), - '*.{0}'.format(wo_domain)] - else: - Log.info(self, "Certificate type : domain") - acme_domains = \ - acme_domains + ['{0}'.format(wo_domain), - 'www.{0}'.format(wo_domain)] - - if not os.path.isfile("{0}/conf/nginx/ssl.conf.disabled"): - if acme_subdomain: - Log.debug(self, "checkWildcardExist on *.{0}" - .format(wo_root_domain)) - if SSL.checkwildcardexist(self, wo_root_domain): - Log.info( - self, "Using existing Wildcard SSL " - "certificate from {0} to secure {1}" - .format(wo_root_domain, wo_domain)) - Log.debug( - self, "symlink wildcard " - "cert between {0} & {1}" - .format(wo_domain, wo_root_domain)) - # copy the cert from the root domain - copyWildcardCert(self, wo_domain, - wo_root_domain) - else: - # check DNS records before issuing cert - if not acmedata['dns'] is True: - if not pargs.force: - if not WOAcme.check_dns(self, - acme_domains): - Log.error( - self, - "Aborting SSL certificate " - "issuance") - Log.debug(self, "Setup Cert with acme.sh for {0}" - .format(wo_domain)) - if WOAcme.setupletsencrypt( - self, acme_domains, acmedata): - WOAcme.deploycert(self, wo_domain) - else: - Log.error(self, "Unable to issue certificate") - else: - # check DNS records before issuing cert - if not acmedata['dns'] is True: - if not pargs.force: - if not WOAcme.check_dns(self, - acme_domains): - Log.error( - self, - "Aborting SSL " - "certificate issuance") - if WOAcme.setupletsencrypt( - self, acme_domains, acmedata): - WOAcme.deploycert(self, wo_domain) - else: - Log.error(self, "Unable to issue certificate") - else: - WOFileUtils.mvfile( - self, "{0}/conf/nginx/ssl.conf.disabled" - .format(wo_site_webroot), - '{0}/conf/nginx/ssl.conf' - .format(wo_site_webroot)) - WOFileUtils.mvfile( - self, "/etc/nginx/conf.d/" - "force-ssl-{0}.conf.disabled" - .format(wo_domain), - '/etc/nginx/conf.d/force-ssl-{0}.conf' - .format(wo_domain)) - - httpsRedirect(self, wo_domain, True, acme_wildcard) - SSL.siteurlhttps(self, wo_domain) - - if not WOService.reload_service(self, 'nginx'): - Log.error(self, "service nginx reload failed. " - "check issues with `nginx -t` command") - Log.info(self, "Congratulations! Successfully " - "Configured SSL for Site " - " https://{0}".format(wo_domain)) - if (SSL.getexpirationdays(self, wo_domain) > 0): - Log.info(self, "Your cert will expire within " + - str(SSL.getexpirationdays(self, wo_domain)) + - " days.") - else: - Log.warn( - self, "Your cert already EXPIRED ! " - ".PLEASE renew soon . ") - - elif data['letsencrypt'] is False: - if pargs.letsencrypt == "off": - if os.path.islink("{0}/conf/nginx/ssl.conf" - .format(wo_site_webroot)): - WOFileUtils.remove_symlink(self, - "{0}/conf/nginx/ssl.conf" - .format(wo_site_webroot)) - elif os.path.isfile("{0}/conf/nginx/ssl.conf" - .format(wo_site_webroot)): - Log.info(self, 'Setting Nginx configuration') - WOFileUtils.mvfile(self, "{0}/conf/nginx/ssl.conf" - .format(wo_site_webroot), - '{0}/conf/nginx/ssl.conf.disabled' - .format(wo_site_webroot)) - httpsRedirect(self, wo_domain, False) - if os.path.isfile("{0}/conf/nginx/hsts.conf" - .format(wo_site_webroot)): - WOFileUtils.mvfile(self, "{0}/conf/nginx/hsts.conf" - .format(wo_site_webroot), - '{0}/conf/nginx/' - 'hsts.conf.disabled' - .format(wo_site_webroot)) - # find all broken symlinks - sympath = "/var/www" - WOFileUtils.findBrokenSymlink(self, sympath) - - elif (pargs.letsencrypt == "clean" or - pargs.letsencrypt == "purge"): - WOAcme.removeconf(self, wo_domain) - # find all broken symlinks - sympath = "/var/www" - WOFileUtils.findBrokenSymlink(self, sympath) - if not WOService.reload_service(self, 'nginx'): - Log.error(self, "service nginx reload failed. " - "check issues with `nginx -t` command") - # Log.info(self,"Removing Cron Job set for cert - # auto-renewal") WOCron.remove_cron(self,'wo site - # update {0} --le=renew --min_expiry_limit 30 - # 2> \/dev\/null'.format(wo_domain)) - Log.info(self, "Successfully Disabled SSl for Site " - " http://{0}".format(wo_domain)) - - # Add nginx conf folder into GIT - WOGit.add(self, ["{0}/conf/nginx".format(wo_site_webroot)], - msg="Adding letsencrypts config of site: {0}" - .format(wo_domain)) - updateSiteInfo(self, wo_domain, ssl=letsencrypt) - return 0 - - if pargs.hsts: - if data['hsts'] is True: - if os.path.isfile(("{0}/conf/nginx/ssl.conf") - .format(wo_site_webroot)): - if not os.path.isfile("{0}/conf/nginx/hsts.conf" - .format(wo_site_webroot)): - SSL.setuphsts(self, wo_domain) - else: - Log.error(self, "HSTS is already configured for given " - "site") - if not WOService.reload_service(self, 'nginx'): - Log.error(self, "service nginx reload failed. " - "check issues with `nginx -t` command") - else: - Log.error(self, "HTTPS is not configured for given " - "site") - - elif data['hsts'] is False: - if os.path.isfile(("{0}/conf/nginx/hsts.conf") - .format(wo_site_webroot)): - WOFileUtils.mvfile(self, "{0}/conf/nginx/hsts.conf" - .format(wo_site_webroot), - '{0}/conf/nginx/hsts.conf.disabled' - .format(wo_site_webroot)) - if not WOService.reload_service(self, 'nginx'): - Log.error(self, "service nginx reload failed. " - "check issues with `nginx -t` command") - else: - Log.error(self, "HSTS is not configured for given " - "site") - if pargs.ngxblocker: - if ngxblocker is True: - setupngxblocker(self, wo_domain) - elif ngxblocker is False: - if os.path.isfile("{0}/conf/nginx/ngxblocker.conf" - .format(wo_site_webroot)): - WOFileUtils.mvfile( - self, - "{0}/conf/nginx/ngxblocker.conf" - .format(wo_site_webroot), - "{0}/conf/nginx/ngxblocker.conf.disabled" - .format(wo_site_webroot)) - # Service Nginx Reload - if not WOService.reload_service(self, 'nginx'): - Log.error(self, "service nginx reload failed. " - "check issues with `nginx -t` command") - - if stype == oldsitetype and cache == oldcachetype: - - # Service Nginx Reload - if not WOService.reload_service(self, 'nginx'): - Log.error(self, "service nginx reload failed. " - "check issues with `nginx -t` command") - - updateSiteInfo(self, wo_domain, stype=stype, cache=cache, - ssl=(bool(check_site.is_ssl)), - php_version=check_php_version) - - Log.info(self, "Successfully updated site" - " http://{0}".format(wo_domain)) - return 0 - - # if data['wo_db_name'] and not data['wp']: - if 'wo_db_name' in data.keys() and not data['wp']: - try: - data = setupdatabase(self, data) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Update site failed." - "Check the log for details:" - "`tail /var/log/wo/wordops.log` and please try again") - return 1 - try: - wodbconfig = open("{0}/wo-config.php".format(wo_site_webroot), - encoding='utf-8', mode='w') - wodbconfig.write("" - .format(data['wo_db_name'], - data['wo_db_user'], - data['wo_db_pass'], - data['wo_db_host'])) - wodbconfig.close() - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "creating wo-config.php failed.") - Log.info(self, Log.FAIL + "Update site failed. " - "Check the log for details: " - "`tail /var/log/wo/wordops.log` and please try again") - return 1 - - # Setup WordPress if old sites are html/php/mysql sites - if data['wp'] and oldsitetype in ['html', 'proxy', 'php', - 'mysql', 'php73']: - try: - wo_wp_creds = setupwordpress(self, data) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Update site failed." - "Check the log for details: " - "`tail /var/log/wo/wordops.log` and please try again") - return 1 - - # Uninstall unnecessary plugins - if oldsitetype in ['wp', 'wpsubdir', 'wpsubdomain']: - # Setup WordPress Network if update option is multisite - # and oldsite is WordPress single site - if data['multisite'] and oldsitetype == 'wp': - try: - setupwordpressnetwork(self, data) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Update site failed. " - "Check the log for details:" - " `tail /var/log/wo/wordops.log` " - "and please try again") - return 1 - - if ((oldcachetype in ['wpsc', 'basic', 'wpredis', 'wprocket', - 'wpce'] and - (data['wpfc'])) or (oldsitetype == 'wp' and - data['multisite'] and data['wpfc'])): - try: - plugin_data_object = { - "log_level": "INFO", - "log_filesize": 5, - "enable_purge": 1, - "enable_map": "0", - "enable_log": 0, - "enable_stamp": 1, - "purge_homepage_on_new": 1, - "purge_homepage_on_edit": 1, - "purge_homepage_on_del": 1, - "purge_archive_on_new": 1, - "purge_archive_on_edit": 0, - "purge_archive_on_del": 0, - "purge_archive_on_new_comment": 0, - "purge_archive_on_deleted_comment": 0, - "purge_page_on_mod": 1, - "purge_page_on_new_comment": 1, - "purge_page_on_deleted_comment": 1, - "cache_method": "enable_fastcgi", - "purge_method": "get_request", - "redis_hostname": "127.0.0.1", - "redis_port": "6379", - "redis_prefix": "nginx-cache:"} - plugin_data = json.dumps(plugin_data_object) - setupwp_plugin(self, 'nginx-helper', - 'rt_wp_nginx_helper_options', - plugin_data, data) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Update nginx-helper " - "settings failed. " - "Check the log for details:" - " `tail /var/log/wo/wordops.log` " - "and please try again") - return 1 - - elif ((oldcachetype in ['wpsc', 'basic', 'wpfc', - 'wprocket', 'wpce'] and - (data['wpredis'])) or (oldsitetype == 'wp' and - data['multisite'] and - data['wpredis'])): - try: - plugin_data_object = { - "log_level": "INFO", - "log_filesize": 5, - "enable_purge": 1, - "enable_map": "0", - "enable_log": 0, - "enable_stamp": 1, - "purge_homepage_on_new": 1, - "purge_homepage_on_edit": 1, - "purge_homepage_on_del": 1, - "purge_archive_on_new": 1, - "purge_archive_on_edit": 0, - "purge_archive_on_del": 0, - "purge_archive_on_new_comment": 0, - "purge_archive_on_deleted_comment": 0, - "purge_page_on_mod": 1, - "purge_page_on_new_comment": 1, - "purge_page_on_deleted_comment": 1, - "cache_method": "enable_redis", - "purge_method": "get_request", - "redis_hostname": "127.0.0.1", - "redis_port": "6379", - "redis_prefix": "nginx-cache:"} - plugin_data = json.dumps(plugin_data_object) - setupwp_plugin(self, 'nginx-helper', - 'rt_wp_nginx_helper_options', - plugin_data, data) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Update nginx-helper " - "settings failed. " - "Check the log for details:" - " `tail /var/log/wo/wordops.log` " - "and please try again") - return 1 - else: - try: - # disable nginx-helper - plugin_data_object = { - "log_level": "INFO", - "log_filesize": 5, - "enable_purge": 0, - "enable_map": 0, - "enable_log": 0, - "enable_stamp": 0, - "purge_homepage_on_new": 1, - "purge_homepage_on_edit": 1, - "purge_homepage_on_del": 1, - "purge_archive_on_new": 1, - "purge_archive_on_edit": 0, - "purge_archive_on_del": 0, - "purge_archive_on_new_comment": 0, - "purge_archive_on_deleted_comment": 0, - "purge_page_on_mod": 1, - "purge_page_on_new_comment": 1, - "purge_page_on_deleted_comment": 1, - "cache_method": "enable_redis", - "purge_method": "get_request", - "redis_hostname": "127.0.0.1", - "redis_port": "6379", - "redis_prefix": "nginx-cache:"} - plugin_data = json.dumps(plugin_data_object) - setupwp_plugin( - self, 'nginx-helper', - 'rt_wp_nginx_helper_options', plugin_data, data) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Update nginx-helper " - "settings failed. " - "Check the log for details:" - " `tail /var/log/wo/wordops.log` " - "and please try again") - return 1 - - if ((oldcachetype in ['wpsc', 'basic', - 'wpfc', 'wprocket', 'wpredis'] and - (data['wpce'])) or (oldsitetype == 'wp' and - data['multisite'] and - data['wpce'])): - try: - installwp_plugin(self, 'cache-enabler', data) - # setup cache-enabler - plugin_data_object = { - "expires": 24, - "new_post": 1, - "new_comment": 0, - "webp": 0, - "clear_on_upgrade": 1, - "compress": 0, - "excl_ids": "", - "excl_regexp": "", - "excl_cookies": "", - "incl_attributes": "", - "minify_html": 1} - plugin_data = json.dumps(plugin_data_object) - setupwp_plugin(self, 'cache-enabler', - 'cache-enabler', plugin_data, data) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Update cache-enabler " - "settings failed. " - "Check the log for details:" - " `tail /var/log/wo/wordops.log` " - "and please try again") - return 1 - - if oldcachetype == 'wpsc' and not data['wpsc']: - try: - uninstallwp_plugin(self, 'wp-super-cache', data) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Update site failed." - "Check the log for details:" - " `tail /var/log/wo/wordops.log` " - "and please try again") - return 1 - - if oldcachetype == 'wpredis' and not data['wpredis']: - try: - uninstallwp_plugin(self, 'redis-cache', data) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Update site failed." - "Check the log for details:" - " `tail /var/log/wo/wordops.log` " - "and please try again") - return 1 - - if oldcachetype != 'wpsc' and data['wpsc']: - try: - installwp_plugin(self, 'wp-super-cache', data) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Update site failed." - "Check the log for details: " - "`tail /var/log/wo/wordops.log` and please try again") - return 1 - - if oldcachetype == 'wprocket' and not data['wprocket']: - try: - uninstallwp_plugin(self, 'wp-rocket', data) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Update site failed." - "Check the log for details: " - "`tail /var/log/wo/wordops.log` and please try again") - return 1 - - if oldcachetype == 'wpce' and not data['wpce']: - try: - uninstallwp_plugin(self, 'cache-enabler', data) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Update site failed." - "Check the log for details: " - "`tail /var/log/wo/wordops.log` and please try again") - return 1 - - # Service Nginx Reload - if not WOService.reload_service(self, 'nginx'): - Log.error(self, "service nginx reload failed. " - "check issues with `nginx -t` command") - - WOGit.add(self, ["/etc/nginx"], - msg="{0} updated with {1} {2}" - .format(wo_www_domain, stype, cache)) - # Setup Permissions for webroot - try: - setwebrootpermissions(self, data['webroot']) - except SiteError as e: - Log.debug(self, str(e)) - Log.info(self, Log.FAIL + "Update site failed." - "Check the log for details: " - "`tail /var/log/wo/wordops.log` and please try again") - return 1 - - if wo_auth and len(wo_auth): - for msg in wo_auth: - Log.info(self, Log.ENDC + msg) - - display_cache_settings(self, data) - if data['wp'] and oldsitetype in ['html', 'php', 'mysql']: - Log.info(self, "\n\n" + Log.ENDC + "WordPress admin user :" - " {0}".format(wo_wp_creds['wp_user'])) - Log.info(self, Log.ENDC + "WordPress admin password : {0}" - .format(wo_wp_creds['wp_pass']) + "\n\n") - if oldsitetype in ['html', 'php'] and stype != 'php': - updateSiteInfo(self, wo_domain, stype=stype, cache=cache, - db_name=data['wo_db_name'], - db_user=data['wo_db_user'], - db_password=data['wo_db_pass'], - db_host=data['wo_db_host'], - ssl=True if check_site.is_ssl else False, - php_version=check_php_version) - else: - updateSiteInfo(self, wo_domain, stype=stype, cache=cache, - ssl=True if check_site.is_ssl else False, - php_version=check_php_version) - Log.info(self, "Successfully updated site" - " http://{0}".format(wo_domain)) - return 0 - - class WOSiteDeleteController(CementBaseController): class Meta: label = 'delete' @@ -1977,7 +364,8 @@ class WOSiteDeleteController(CementBaseController): wo_db_name = check_site.db_name wo_db_user = check_site.db_user if self.app.config.has_section('mysql'): - wo_mysql_grant_host = self.app.config.get('mysql', 'grant-host') + wo_mysql_grant_host = self.app.config.get( + 'mysql', 'grant-host') else: wo_mysql_grant_host = 'localhost' if wo_db_name == 'deleted': @@ -2093,9 +481,9 @@ class WOSiteListController(CementBaseController): def load(app): # register the plugin class.. this only happens if the plugin is enabled app.handler.register(WOSiteController) - app.handler.register(WOSiteCreateController) - app.handler.register(WOSiteUpdateController) app.handler.register(WOSiteDeleteController) + app.handler.register(WOSiteUpdateController) + app.handler.register(WOSiteCreateController) app.handler.register(WOSiteListController) app.handler.register(WOSiteEditController) # register a hook (function) to run after arguments are parsed. diff --git a/wo/cli/plugins/site_create.py b/wo/cli/plugins/site_create.py new file mode 100644 index 0000000..6152d2c --- /dev/null +++ b/wo/cli/plugins/site_create.py @@ -0,0 +1,551 @@ +import os + +from cement.core.controller import CementBaseController, expose +from wo.cli.plugins.site_functions import * +from wo.cli.plugins.sitedb import (addNewSite, deleteSiteInfo, + updateSiteInfo) +from wo.core.acme import WOAcme +from wo.core.domainvalidate import WODomain +from wo.core.git import WOGit +from wo.core.logging import Log +from wo.core.nginxhashbucket import hashbucket +from wo.core.services import WOService +from wo.core.sslutils import SSL +from wo.core.variables import WOVar + + +class WOSiteCreateController(CementBaseController): + class Meta: + label = 'create' + stacked_on = 'site' + stacked_type = 'nested' + description = ('this commands set up configuration and installs ' + 'required files as options are provided') + arguments = [ + (['site_name'], + dict(help='domain name for the site to be created.', + nargs='?')), + (['--html'], + dict(help="create html site", action='store_true')), + (['--php'], + dict(help="create php 7.2 site", action='store_true')), + (['--php72'], + dict(help="create php 7.2 site", action='store_true')), + (['--php73'], + dict(help="create php 7.3 site", action='store_true')), + (['--php74'], + dict(help="create php 7.4 site", action='store_true')), + (['--mysql'], + dict(help="create mysql site", action='store_true')), + (['--wp'], + dict(help="create WordPress single site", + action='store_true')), + (['--wpsubdir'], + dict(help="create WordPress multisite with subdirectory setup", + action='store_true')), + (['--wpsubdomain'], + dict(help="create WordPress multisite with subdomain setup", + action='store_true')), + (['--wpfc'], + dict(help="create WordPress single/multi site with " + "Nginx fastcgi_cache", + action='store_true')), + (['--wpsc'], + dict(help="create WordPress single/multi site with wpsc cache", + action='store_true')), + (['--wprocket'], + dict(help="create WordPress single/multi site with WP-Rocket", + action='store_true')), + (['--wpce'], + dict(help="create WordPress single/multi site with Cache-Enabler", + action='store_true')), + (['--wpredis'], + dict(help="create WordPress single/multi site " + "with redis cache", + action='store_true')), + (['-le', '--letsencrypt'], + dict(help="configure letsencrypt ssl for the site", + action='store' or 'store_const', + choices=('on', 'subdomain', 'wildcard'), + const='on', nargs='?')), + (['--force'], + dict(help="force Let's Encrypt certificate issuance", + action='store_true')), + (['--dns'], + dict(help="choose dns provider api for letsencrypt", + action='store' or 'store_const', + const='dns_cf', nargs='?')), + (['--dnsalias'], + dict(help="set domain used for acme dns alias validation", + action='store', nargs='?')), + (['--hsts'], + dict(help="enable HSTS for site secured with letsencrypt", + action='store_true')), + (['--ngxblocker'], + dict(help="enable HSTS for site secured with letsencrypt", + action='store_true')), + (['--user'], + dict(help="provide user for WordPress site")), + (['--email'], + dict(help="provide email address for WordPress site")), + (['--pass'], + dict(help="provide password for WordPress user", + dest='wppass')), + (['--proxy'], + dict(help="create proxy for site", nargs='+')), + (['--vhostonly'], dict(help="only create vhost and database " + "without installing WordPress", + action='store_true')), + ] + + @expose(hide=True) + def default(self): + pargs = self.app.pargs + # self.app.render((data), 'default.mustache') + # Check domain name validation + data = dict() + host, port = None, None + try: + stype, cache = detSitePar(vars(pargs)) + except RuntimeError as e: + Log.debug(self, str(e)) + Log.error(self, "Please provide valid options to creating site") + + if stype is None and pargs.proxy: + stype, cache = 'proxy', '' + proxyinfo = pargs.proxy[0].strip() + if not proxyinfo: + Log.error(self, "Please provide proxy server host information") + proxyinfo = proxyinfo.split(':') + host = proxyinfo[0].strip() + port = '80' if len(proxyinfo) < 2 else proxyinfo[1].strip() + elif stype is None and not pargs.proxy: + stype, cache = 'html', 'basic' + elif stype and pargs.proxy: + Log.error(self, "proxy should not be used with other site types") + + if not pargs.site_name: + try: + while not pargs.site_name: + # preprocessing before finalize site name + pargs.site_name = (input('Enter site name : ') + .strip()) + except IOError as e: + Log.debug(self, str(e)) + Log.error(self, "Unable to input site name, Please try again!") + + pargs.site_name = pargs.site_name.strip() + wo_domain = WODomain.validate(self, pargs.site_name) + wo_www_domain = "www.{0}".format(wo_domain) + (wo_domain_type, wo_root_domain) = WODomain.getlevel( + self, wo_domain) + if not wo_domain.strip(): + Log.error(self, "Invalid domain name, " + "Provide valid domain name") + + wo_site_webroot = WOVar.wo_webroot + wo_domain + + if check_domain_exists(self, wo_domain): + Log.error(self, "site {0} already exists".format(wo_domain)) + elif os.path.isfile('/etc/nginx/sites-available/{0}' + .format(wo_domain)): + Log.error(self, "Nginx configuration /etc/nginx/sites-available/" + "{0} already exists".format(wo_domain)) + + if stype == 'proxy': + data = dict( + site_name=wo_domain, www_domain=wo_www_domain, + static=True, basic=False, wp=False, + wpfc=False, wpsc=False, wprocket=False, wpce=False, + multisite=False, wpsubdir=False, webroot=wo_site_webroot) + data['proxy'] = True + data['host'] = host + data['port'] = port + data['basic'] = True + + if pargs.php72 or pargs.php73 or pargs.php74: + data = dict( + site_name=wo_domain, www_domain=wo_www_domain, + static=False, basic=False, + wp=False, wpfc=False, wpsc=False, wprocket=False, + wpce=False, multisite=False, + wpsubdir=False, webroot=wo_site_webroot) + data['basic'] = True + + if stype in ['html', 'php']: + data = dict( + site_name=wo_domain, www_domain=wo_www_domain, + static=True, basic=False, wp=False, + wpfc=False, wpsc=False, wprocket=False, wpce=False, + multisite=False, wpsubdir=False, webroot=wo_site_webroot) + + if stype == 'php': + data['static'] = False + data['basic'] = True + + elif stype in ['mysql', 'wp', 'wpsubdir', 'wpsubdomain']: + + data = dict( + site_name=wo_domain, www_domain=wo_www_domain, + static=False, basic=True, wp=False, wpfc=False, + wpsc=False, wpredis=False, wprocket=False, wpce=False, + multisite=False, wpsubdir=False, webroot=wo_site_webroot, + wo_db_name='', wo_db_user='', wo_db_pass='', + wo_db_host='') + + if stype in ['wp', 'wpsubdir', 'wpsubdomain']: + data['wp'] = True + data['basic'] = False + data[cache] = True + data['wp-user'] = pargs.user + data['wp-email'] = pargs.email + data['wp-pass'] = pargs.wppass + if stype in ['wpsubdir', 'wpsubdomain']: + data['multisite'] = True + if stype == 'wpsubdir': + data['wpsubdir'] = True + else: + pass + + if data and pargs.php73: + data['php73'] = True + data['php74'] = False + data['php72'] = False + data['wo_php'] = 'php73' + elif data and pargs.php74: + data['php72'] = False + data['php74'] = True + data['php73'] = False + data['wo_php'] = 'php74' + else: + data['php74'] = False + data['php72'] = True + data['php73'] = False + data['wo_php'] = 'php72' + + if ((not pargs.wpfc) and (not pargs.wpsc) and + (not pargs.wprocket) and + (not pargs.wpce) and + (not pargs.wpredis)): + data['basic'] = True + + if (cache == 'wpredis'): + cache = 'wpredis' + data['wpredis'] = True + data['basic'] = False + pargs.wpredis = True + + # Check rerequired packages are installed or not + wo_auth = site_package_check(self, stype) + + try: + pre_run_checks(self) + except SiteError as e: + Log.debug(self, str(e)) + Log.error(self, "NGINX configuration check failed.") + + try: + try: + # setup NGINX configuration, and webroot + setupdomain(self, data) + + # Fix Nginx Hashbucket size error + hashbucket(self) + except SiteError as e: + # call cleanup actions on failure + Log.info(self, Log.FAIL + + "There was a serious error encountered...") + Log.info(self, Log.FAIL + "Cleaning up afterwards...") + doCleanupAction(self, domain=wo_domain, + webroot=data['webroot']) + Log.debug(self, str(e)) + Log.error(self, "Check the log for details: " + "`tail /var/log/wo/wordops.log` " + "and please try again") + + if 'proxy' in data.keys() and data['proxy']: + addNewSite(self, wo_domain, stype, cache, wo_site_webroot) + # Service Nginx Reload + if not WOService.reload_service(self, 'nginx'): + Log.info(self, Log.FAIL + + "There was a serious error encountered...") + Log.info(self, Log.FAIL + "Cleaning up afterwards...") + doCleanupAction(self, domain=wo_domain) + deleteSiteInfo(self, wo_domain) + Log.error(self, "service nginx reload failed. " + "check issues with `nginx -t` command") + Log.error(self, "Check the log for details: " + "`tail /var/log/wo/wordops.log` " + "and please try again") + if wo_auth and len(wo_auth): + for msg in wo_auth: + Log.info(self, Log.ENDC + msg, log=False) + Log.info(self, "Successfully created site" + " http://{0}".format(wo_domain)) + return + + if data['php73']: + php_version = "7.3" + elif data['php74']: + php_version = "7.4" + else: + php_version = "7.2" + + addNewSite(self, wo_domain, stype, cache, wo_site_webroot, + php_version=php_version) + + # Setup database for MySQL site + if 'wo_db_name' in data.keys() and not data['wp']: + try: + data = setupdatabase(self, data) + # Add database information for site into database + updateSiteInfo(self, wo_domain, db_name=data['wo_db_name'], + db_user=data['wo_db_user'], + db_password=data['wo_db_pass'], + db_host=data['wo_db_host']) + except SiteError as e: + # call cleanup actions on failure + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + + "There was a serious error encountered...") + Log.info(self, Log.FAIL + "Cleaning up afterwards...") + doCleanupAction(self, domain=wo_domain, + webroot=data['webroot'], + dbname=data['wo_db_name'], + dbuser=data['wo_db_user'], + dbhost=data['wo_db_host']) + deleteSiteInfo(self, wo_domain) + Log.error(self, "Check the log for details: " + "`tail /var/log/wo/wordops.log` " + "and please try again") + + try: + wodbconfig = open("{0}/wo-config.php" + .format(wo_site_webroot), + encoding='utf-8', mode='w') + wodbconfig.write("" + .format(data['wo_db_name'], + data['wo_db_user'], + data['wo_db_pass'], + data['wo_db_host'])) + wodbconfig.close() + stype = 'mysql' + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "wo-config.php") + Log.info(self, Log.FAIL + + "There was a serious error encountered...") + Log.info(self, Log.FAIL + "Cleaning up afterwards...") + doCleanupAction(self, domain=wo_domain, + webroot=data['webroot'], + dbname=data['wo_db_name'], + dbuser=data['wo_db_user'], + dbhost=data['wo_db_host']) + deleteSiteInfo(self, wo_domain) + Log.error(self, "Check the log for details: " + "`tail /var/log/wo/wordops.log` " + "and please try again") + + # Setup WordPress if Wordpress site + if data['wp']: + vhostonly = bool(pargs.vhostonly) + try: + wo_wp_creds = setupwordpress(self, data, vhostonly) + # Add database information for site into database + updateSiteInfo(self, wo_domain, + db_name=data['wo_db_name'], + db_user=data['wo_db_user'], + db_password=data['wo_db_pass'], + db_host=data['wo_db_host']) + except SiteError as e: + # call cleanup actions on failure + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + + "There was a serious error encountered...") + Log.info(self, Log.FAIL + "Cleaning up afterwards...") + doCleanupAction(self, domain=wo_domain, + webroot=data['webroot'], + dbname=data['wo_db_name'], + dbuser=data['wo_db_user'], + dbhost=data['wo_mysql_grant_host']) + deleteSiteInfo(self, wo_domain) + Log.error(self, "Check the log for details: " + "`tail /var/log/wo/wordops.log` " + "and please try again") + + # Service Nginx Reload call cleanup if failed to reload nginx + if not WOService.reload_service(self, 'nginx'): + Log.info(self, Log.FAIL + + "There was a serious error encountered...") + Log.info(self, Log.FAIL + "Cleaning up afterwards...") + doCleanupAction(self, domain=wo_domain, + webroot=data['webroot']) + if 'wo_db_name' in data.keys(): + doCleanupAction(self, domain=wo_domain, + dbname=data['wo_db_name'], + dbuser=data['wo_db_user'], + dbhost=data['wo_mysql_grant_host']) + deleteSiteInfo(self, wo_domain) + Log.info(self, Log.FAIL + "service nginx reload failed." + " check issues with `nginx -t` command.") + Log.error(self, "Check the log for details: " + "`tail /var/log/wo/wordops.log` " + "and please try again") + + WOGit.add(self, ["/etc/nginx"], + msg="{0} created with {1} {2}" + .format(wo_www_domain, stype, cache)) + # Setup Permissions for webroot + try: + setwebrootpermissions(self, data['webroot']) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + + "There was a serious error encountered...") + Log.info(self, Log.FAIL + "Cleaning up afterwards...") + doCleanupAction(self, domain=wo_domain, + webroot=data['webroot']) + if 'wo_db_name' in data.keys(): + print("Inside db cleanup") + doCleanupAction(self, domain=wo_domain, + dbname=data['wo_db_name'], + dbuser=data['wo_db_user'], + dbhost=data['wo_mysql_grant_host']) + deleteSiteInfo(self, wo_domain) + Log.error(self, "Check the log for details: " + "`tail /var/log/wo/wordops.log` and " + "please try again") + + if wo_auth and len(wo_auth): + for msg in wo_auth: + Log.info(self, Log.ENDC + msg, log=False) + + if data['wp'] and (not pargs.vhostonly): + Log.info(self, Log.ENDC + "WordPress admin user :" + " {0}".format(wo_wp_creds['wp_user']), log=False) + Log.info(self, Log.ENDC + "WordPress admin password : {0}" + .format(wo_wp_creds['wp_pass']), log=False) + + display_cache_settings(self, data) + + Log.info(self, "Successfully created site" + " http://{0}".format(wo_domain)) + except SiteError: + Log.error(self, "Check the log for details: " + "`tail /var/log/wo/wordops.log` and please try again") + + if pargs.letsencrypt: + acme_domains = [] + data['letsencrypt'] = True + letsencrypt = True + Log.debug(self, "Going to issue Let's Encrypt certificate") + acmedata = dict( + acme_domains, dns=False, acme_dns='dns_cf', + dnsalias=False, acme_alias='', keylength='') + if self.app.config.has_section('letsencrypt'): + acmedata['keylength'] = self.app.config.get( + 'letsencrypt', 'keylength') + else: + acmedata['keylength'] = 'ec-384' + if pargs.dns: + Log.debug(self, "DNS validation enabled") + acmedata['dns'] = True + if not pargs.dns == 'dns_cf': + Log.debug(self, "DNS API : {0}".format(pargs.dns)) + acmedata['acme_dns'] = pargs.dns + if pargs.dnsalias: + Log.debug(self, "DNS Alias enabled") + acmedata['dnsalias'] = True + acmedata['acme_alias'] = pargs.dnsalias + + # detect subdomain and set subdomain variable + if pargs.letsencrypt == "subdomain": + Log.warn( + self, 'Flag --letsencrypt=subdomain is ' + 'deprecated and not required anymore.') + acme_subdomain = True + acme_wildcard = False + elif pargs.letsencrypt == "wildcard": + acme_wildcard = True + acme_subdomain = False + acmedata['dns'] = True + else: + if ((wo_domain_type == 'subdomain')): + Log.debug(self, "Domain type = {0}" + .format(wo_domain_type)) + acme_subdomain = True + else: + acme_subdomain = False + acme_wildcard = False + + if acme_subdomain is True: + Log.info(self, "Certificate type : subdomain") + acme_domains = acme_domains + ['{0}'.format(wo_domain)] + elif acme_wildcard is True: + Log.info(self, "Certificate type : wildcard") + acme_domains = acme_domains + ['{0}'.format(wo_domain), + '*.{0}'.format(wo_domain)] + else: + Log.info(self, "Certificate type : domain") + acme_domains = acme_domains + ['{0}'.format(wo_domain), + 'www.{0}'.format(wo_domain)] + + if WOAcme.cert_check(self, wo_domain): + SSL.archivedcertificatehandle(self, wo_domain, acme_domains) + else: + if acme_subdomain is True: + # check if a wildcard cert for the root domain exist + Log.debug(self, "checkWildcardExist on *.{0}" + .format(wo_root_domain)) + if SSL.checkwildcardexist(self, wo_root_domain): + Log.info(self, "Using existing Wildcard SSL " + "certificate from {0} to secure {1}" + .format(wo_root_domain, wo_domain)) + Log.debug(self, "symlink wildcard " + "cert between {0} & {1}" + .format(wo_domain, wo_root_domain)) + # copy the cert from the root domain + copyWildcardCert(self, wo_domain, wo_root_domain) + else: + # check DNS records before issuing cert + if not acmedata['dns'] is True: + if not pargs.force: + if not WOAcme.check_dns(self, acme_domains): + Log.error(self, + "Aborting SSL " + "certificate issuance") + Log.debug(self, "Setup Cert with acme.sh for {0}" + .format(wo_domain)) + if WOAcme.setupletsencrypt( + self, acme_domains, acmedata): + WOAcme.deploycert(self, wo_domain) + else: + if not acmedata['dns'] is True: + if not pargs.force: + if not WOAcme.check_dns(self, acme_domains): + Log.error(self, + "Aborting SSL certificate issuance") + if WOAcme.setupletsencrypt( + self, acme_domains, acmedata): + WOAcme.deploycert(self, wo_domain) + + if pargs.hsts: + SSL.setuphsts(self, wo_domain) + + SSL.httpsredirect(self, wo_domain, acme_domains, True) + SSL.siteurlhttps(self, wo_domain) + if not WOService.reload_service(self, 'nginx'): + Log.error(self, "service nginx reload failed. " + "check issues with `nginx -t` command") + Log.info(self, "Congratulations! Successfully Configured " + "SSL on https://{0}".format(wo_domain)) + + # Add nginx conf folder into GIT + WOGit.add(self, ["{0}/conf/nginx".format(wo_site_webroot)], + msg="Adding letsencrypts config of site: {0}" + .format(wo_domain)) + updateSiteInfo(self, wo_domain, ssl=letsencrypt) diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index 3cbdc1f..b61f6fc 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -38,7 +38,7 @@ class SiteError(Exception): def pre_run_checks(self): # Check nginx configuration - Log.wait(self, "Running pre-update checks") + Log.wait(self, "Running pre-run checks") try: Log.debug(self, "checking NGINX configuration ...") fnull = open('/dev/null', 'w') @@ -78,12 +78,8 @@ def setupdomain(self, data): wo_site_nginx_conf = open('/etc/nginx/sites-available/{0}' .format(wo_domain_name), encoding='utf-8', mode='w') - if not data['php73']: - self.app.render((data), 'virtualconf.mustache', - out=wo_site_nginx_conf) - else: - self.app.render((data), 'virtualconf-php7.mustache', - out=wo_site_nginx_conf) + self.app.render((data), 'virtualconf.mustache', + out=wo_site_nginx_conf) wo_site_nginx_conf.close() except IOError as e: Log.debug(self, str(e)) @@ -148,7 +144,9 @@ def setupdatabase(self, data): wo_random_pass = (''.join(random.sample(string.ascii_uppercase + string.ascii_lowercase + string.digits, 24))) - wo_replace_dot = wo_domain_name.replace('.', '') + wo_replace_dash = wo_domain_name.replace('-', '_') + wo_replace_dot = wo_replace_dash.replace('.', '_') + wo_replace_underscore = wo_replace_dot.replace('_', '') if self.app.config.has_section('mysql'): prompt_dbname = self.app.config.get('mysql', 'db-name') prompt_dbuser = self.app.config.get('mysql', 'db-user') @@ -170,8 +168,7 @@ def setupdatabase(self, data): raise SiteError("Unable to input database name") if not wo_db_name: - wo_db_name = wo_replace_dot - wo_db_name = (wo_db_name[0:16] + generate_random()) + wo_db_name = (wo_replace_dot[0:32] + '_' + generate_8_random()) if prompt_dbuser == 'True' or prompt_dbuser == 'true': try: @@ -184,8 +181,7 @@ def setupdatabase(self, data): raise SiteError("Unable to input database credentials") if not wo_db_username: - wo_db_username = wo_replace_dot - wo_db_username = (wo_db_name[0:12] + generate_random()) + wo_db_username = (wo_replace_underscore[0:12] + generate_random()) if not wo_db_password: wo_db_password = wo_random_pass @@ -195,7 +191,7 @@ def setupdatabase(self, data): try: if WOMysql.check_db_exists(self, wo_db_name): Log.debug(self, "Database already exists, Updating DB_NAME .. ") - wo_db_name = (wo_db_name[0:16] + generate_random()) + wo_db_name = (wo_db_name[0:32] + '_' + generate_8_random()) wo_db_username = (wo_db_name[0:12] + generate_random()) except MySQLConnectionError: raise SiteError("MySQL Connectivity problem occured") @@ -388,7 +384,7 @@ def setupwordpress(self, data, vhostonly=False): ['MEDIA_TRASH', 'true'], ['EMPTY_TRASH_DAYS', '15'], ['WP_AUTO_UPDATE_CORE', 'minor']] - + Log.wait(self, "Configuring WordPress") for wp_conf in wp_conf_variables: wp_var = wp_conf[0] wp_val = wp_conf[1] @@ -403,8 +399,10 @@ def setupwordpress(self, data, vhostonly=False): wp_raw='--raw' if var_raw is True else '')) except CommandExecutionError as e: + Log.failed(self, "Configuring WordPress") Log.debug(self, str(e)) Log.error(self, 'Unable to define wp-config.php variables') + Log.valide(self, "Configuring WordPress") # WOFileUtils.mvfile(self, os.getcwd()+'/wp-config.php', # os.path.abspath(os.path.join(os.getcwd(), os.pardir))) @@ -455,7 +453,7 @@ def setupwordpress(self, data, vhostonly=False): raise SiteError("input WordPress user email failed") Log.debug(self, "Setting up WordPress tables") - + Log.wait(self, "Installing WordPress") if not data['multisite']: Log.debug(self, "Creating tables for WordPress Single site") Log.debug( @@ -478,6 +476,7 @@ def setupwordpress(self, data, vhostonly=False): log=False): pass else: + Log.failed(self, "Installing WordPress") raise SiteError( "setup WordPress tables failed for single site") except CommandExecutionError: @@ -511,11 +510,12 @@ def setupwordpress(self, data, vhostonly=False): log=False): pass else: + Log.failed(self, "Installing WordPress") raise SiteError( "setup WordPress tables failed for wp multi site") except CommandExecutionError: raise SiteError("setup WordPress tables failed for wp multi site") - + Log.valide(self, "Installing WordPress") Log.debug(self, "Updating WordPress permalink") try: WOShellExec.cmd_exec(self, " {0} --allow-root " @@ -774,8 +774,9 @@ def sitebackup(self, data): WOFileUtils.copyfile(self, '/etc/nginx/sites-available/{0}' .format(data['site_name']), backup_path) - if data['currsitetype'] in ['html', 'php', 'proxy', 'mysql']: - if data['php73'] is True and not data['wp']: + if data['currsitetype'] in ['html', 'php', 'php72', 'php74', + 'php73', 'proxy', 'mysql']: + if not data['wp']: Log.info(self, "Backing up Webroot \t\t", end='') WOFileUtils.copyfiles(self, wo_site_webroot + '/htdocs', backup_path + '/htdocs') @@ -832,8 +833,9 @@ def site_package_check(self, stype): packages = [] stack = WOStackController() stack.app = self.app - if stype in ['html', 'proxy', 'php', 'mysql', 'wp', 'wpsubdir', - 'wpsubdomain', 'php73']: + pargs = self.app.pargs + if stype in ['html', 'proxy', 'php', 'php72', 'mysql', 'wp', 'wpsubdir', + 'wpsubdomain', 'php73', 'php74']: Log.debug(self, "Setting apt_packages variable for Nginx") # Check if server has nginx-custom package @@ -846,7 +848,7 @@ def site_package_check(self, stype): Log.info(self, "NGINX PLUS Detected ...") apt = ["nginx-plus"] + WOVar.wo_nginx # apt_packages = apt_packages + WOVar.wo_nginx - stack.post_pref(self, apt, packages) + post_pref(self, apt, packages) elif WOAptGet.is_installed(self, 'nginx'): Log.info(self, "WordOps detected a previously" "installed Nginx package. " @@ -869,69 +871,68 @@ def site_package_check(self, stype): wo_nginx.write('fastcgi_param \tSCRIPT_FILENAME ' '\t$request_filename;\n') - if self.app.pargs.php and self.app.pargs.php73: + if pargs.php and pargs.php73: Log.error( self, "Error: two different PHP versions cannot be " "combined within the same WordOps site") - if not self.app.pargs.php73 and stype in ['php', 'mysql', 'wp', 'wpsubdir', - 'wpsubdomain']: - Log.debug(self, "Setting apt_packages variable for PHP 7.2") - if not WOAptGet.is_installed(self, 'php7.2-fpm'): - if not WOAptGet.is_installed(self, 'php7.3-fpm'): - apt_packages = apt_packages + WOVar.wo_php + \ - WOVar.wo_php_extra - else: - apt_packages = apt_packages + WOVar.wo_php + if pargs.php and pargs.php74: + Log.error( + self, "Error: two different PHP versions cannot be " + "combined within the same WordOps site") - if self.app.pargs.php73 and stype in ['mysql', 'wp', + if pargs.php73 and pargs.php74: + Log.error( + self, "Error: two different PHP versions cannot be " + "combined within the same WordOps site") + + if ((not pargs.php73) and (not pargs.php74) and + stype in ['php', 'php72', 'mysql', 'wp', 'wpsubdir', + 'wpsubdomain']): + Log.debug(self, "Setting apt_packages variable for PHP 7.2") + if not (WOAptGet.is_installed(self, 'php7.2-fpm')): + apt_packages = apt_packages + WOVar.wo_php72 + if not (WOAptGet.is_installed(self, 'php7.3-fpm') or + WOAptGet.is_installed(self, 'php7.4-fpm')): + apt_packages = apt_packages + WOVar.wo_php_extra + + if pargs.php73 and stype in ['php73', 'mysql', 'wp', 'wpsubdir', 'wpsubdomain']: Log.debug(self, "Setting apt_packages variable for PHP 7.3") if not WOAptGet.is_installed(self, 'php7.3-fpm'): - if not WOAptGet.is_installed(self, 'php7.2-fpm'): - apt_packages = apt_packages + WOVar.wo_php + \ - WOVar.wo_php73 + WOVar.wo_php_extra - else: - apt_packages = apt_packages + WOVar.wo_php73 + apt_packages = apt_packages + WOVar.wo_php73 + if not (WOAptGet.is_installed(self, 'php7.2-fpm') or + WOAptGet.is_installed(self, 'php7.4-fpm')): + apt_packages = apt_packages + WOVar.wo_php_extra + + if pargs.php74 and stype in ['php74', 'mysql', 'wp', + 'wpsubdir', 'wpsubdomain']: + Log.debug(self, "Setting apt_packages variable for PHP 7.4") + if not WOAptGet.is_installed(self, 'php7.4-fpm'): + apt_packages = apt_packages + WOVar.wo_php74 + if not (WOAptGet.is_installed(self, 'php7.3-fpm') or + WOAptGet.is_installed(self, 'php7.2-fpm')): + apt_packages = apt_packages + WOVar.wo_php_extra if stype in ['mysql', 'wp', 'wpsubdir', 'wpsubdomain']: Log.debug(self, "Setting apt_packages variable for MySQL") if not WOShellExec.cmd_exec(self, "/usr/bin/mysqladmin ping"): - if not WOVar.wo_distro == 'raspbian': - if (not WOVar.wo_platform_codename == 'jessie'): - wo_mysql = ["mariadb-server", "percona-toolkit", - "python3-mysqldb", "mariadb-backup"] - else: - wo_mysql = ["mariadb-server", "percona-toolkit", - "python3-mysql.connector"] - else: - wo_mysql = ["mariadb-server", "percona-toolkit", - "python3-mysqldb"] - apt_packages = apt_packages + wo_mysql + apt_packages = apt_packages + WOVar.wo_mysql if stype in ['wp', 'wpsubdir', 'wpsubdomain']: Log.debug(self, "Setting packages variable for WP-CLI") - if not WOShellExec.cmd_exec(self, "command -v wp"): + if not WOAptGet.is_exec(self, "wp"): packages = packages + [["https://github.com/wp-cli/wp-cli/" "releases/download/v{0}/" "wp-cli-{0}.phar" .format(WOVar.wo_wp_cli), "/usr/local/bin/wp", "WP-CLI"]] - if self.app.pargs.wpredis: + if pargs.wpredis: Log.debug(self, "Setting apt_packages variable for redis") if not WOAptGet.is_installed(self, 'redis-server'): - apt_packages = apt_packages + ["redis-server"] + apt_packages = apt_packages + WOVar.wo_redis - if self.app.pargs.php73: - Log.debug(self, "Setting apt_packages variable for PHP 7.3") - if not WOAptGet.is_installed(self, 'php7.3-fpm'): - if not WOAptGet.is_installed(self, 'php7.2-fpm'): - apt_packages = apt_packages + WOVar.wo_php + \ - WOVar.wo_php73 + WOVar.wo_php_extra - else: - apt_packages = apt_packages + WOVar.wo_php73 - - if self.app.pargs.ngxblocker: + if pargs.ngxblocker: if not os.path.isdir('/etc/nginx/bots.d'): Log.debug(self, "Setting packages variable for ngxblocker") packages = packages + \ @@ -1093,7 +1094,8 @@ def detSitePar(opts): cachelist = list() for key, val in opts.items(): if val and key in ['html', 'php', 'mysql', 'wp', - 'wpsubdir', 'wpsubdomain', 'php73']: + 'wpsubdir', 'wpsubdomain', 'php72', + 'php73', 'php74']: typelist.append(key) elif val and key in ['wpfc', 'wpsc', 'wpredis', 'wprocket', 'wpce']: cachelist.append(key) @@ -1109,24 +1111,48 @@ def detSitePar(opts): cachetype = 'basic' else: cachetype = cachelist[0] + elif False not in [x in ('php72', 'mysql', 'html') for x in typelist]: + sitetype = 'mysql' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] elif False not in [x in ('php73', 'mysql', 'html') for x in typelist]: sitetype = 'mysql' if not cachelist: cachetype = 'basic' else: cachetype = cachelist[0] + elif False not in [x in ('php74', 'mysql', 'html') for x in typelist]: + sitetype = 'mysql' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] elif False not in [x in ('php', 'mysql') for x in typelist]: sitetype = 'mysql' if not cachelist: cachetype = 'basic' else: cachetype = cachelist[0] + elif False not in [x in ('php72', 'mysql') for x in typelist]: + sitetype = 'mysql' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] elif False not in [x in ('php73', 'mysql') for x in typelist]: sitetype = 'mysql' if not cachelist: cachetype = 'basic' else: cachetype = cachelist[0] + elif False not in [x in ('php74', 'mysql') for x in typelist]: + sitetype = 'mysql' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] elif False not in [x in ('html', 'mysql') for x in typelist]: sitetype = 'mysql' if not cachelist: @@ -1139,12 +1165,6 @@ def detSitePar(opts): cachetype = 'basic' else: cachetype = cachelist[0] - elif False not in [x in ('php73', 'html') for x in typelist]: - sitetype = 'php73' - if not cachelist: - cachetype = 'basic' - else: - cachetype = cachelist[0] elif False not in [x in ('wp', 'wpsubdir') for x in typelist]: sitetype = 'wpsubdir' if not cachelist: @@ -1157,33 +1177,75 @@ def detSitePar(opts): cachetype = 'basic' else: cachetype = cachelist[0] + elif False not in [x in ('wp', 'php72') for x in typelist]: + sitetype = 'wp' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] elif False not in [x in ('wp', 'php73') for x in typelist]: sitetype = 'wp' if not cachelist: cachetype = 'basic' else: cachetype = cachelist[0] + elif False not in [x in ('wp', 'php74') for x in typelist]: + sitetype = 'wp' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('wpsubdir', 'php72') for x in typelist]: + sitetype = 'wpsubdir' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] elif False not in [x in ('wpsubdir', 'php73') for x in typelist]: sitetype = 'wpsubdir' if not cachelist: cachetype = 'basic' else: cachetype = cachelist[0] + elif False not in [x in ('wpsubdir', 'php74') for x in typelist]: + sitetype = 'wpsubdir' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] + elif False not in [x in ('wpsubdomain', 'php72') for x in typelist]: + sitetype = 'wpsubdomain' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] elif False not in [x in ('wpsubdomain', 'php73') for x in typelist]: sitetype = 'wpsubdomain' if not cachelist: cachetype = 'basic' else: cachetype = cachelist[0] + elif False not in [x in ('wpsubdomain', 'php74') for x in typelist]: + sitetype = 'wpsubdomain' + if not cachelist: + cachetype = 'basic' + else: + cachetype = cachelist[0] else: raise RuntimeError("could not determine site and cache type") else: if not typelist and not cachelist: sitetype = None cachetype = None + elif (not typelist or "php72" in typelist) and cachelist: + sitetype = 'wp' + cachetype = cachelist[0] elif (not typelist or "php73" in typelist) and cachelist: sitetype = 'wp' cachetype = cachelist[0] + elif (not typelist or "php74" in typelist) and cachelist: + sitetype = 'wp' + cachetype = cachelist[0] elif typelist and (not cachelist): sitetype = typelist[0] cachetype = 'basic' @@ -1208,6 +1270,13 @@ def generate_random(): return wo_random10 +def generate_8_random(): + wo_random8 = (''.join(random.sample(string.ascii_uppercase + + string.ascii_lowercase + + string.digits, 8))) + return wo_random8 + + def deleteDB(self, dbname, dbuser, dbhost, exit=True): try: # Check if Database exists @@ -1382,170 +1451,6 @@ def renewLetsEncrypt(self, wo_domain_name): # redirect= False to disable https redirection -def httpsRedirect(self, wo_domain_name, redirect=True, wildcard=False): - if redirect: - if os.path.isfile("/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" - .format(wo_domain_name)): - WOFileUtils.mvfile(self, - "/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" - .format(wo_domain_name), - "/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name)) - else: - Log.wait(self, "Adding HTTPS redirection") - if wildcard: - try: - sslconf = open("/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - sslconf.write("server {\n" - "\tlisten 80;\n" + - "\tlisten [::]:80;\n" + - "\tserver_name *.{0} {0};\n" - .format(wo_domain_name) + - "\treturn 301 https://$host" - "$request_uri;\n}") - sslconf.close() - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name)) - - else: - try: - sslconf = open("/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - sslconf.write("server {\n" - "\tlisten 80;\n" + - "\tlisten [::]:80;\n" + - "\tserver_name www.{0} {0};\n" - .format(wo_domain_name) + - "\treturn 301 https://$host" - "$request_uri;\n}") - sslconf.close() - - except IOError as e: - Log.failed(self, "Adding HTTPS redirection") - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name)) - else: - Log.valide(self, "Adding HTTPS redirection") - # Nginx Configation into GIT - WOGit.add(self, - ["/etc/nginx"], msg="Adding /etc/nginx/conf.d/" - "force-ssl-{0}.conf".format(wo_domain_name)) - else: - if os.path.isfile("/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name)): - WOFileUtils.mvfile(self, "/etc/nginx/conf.d/force-ssl-{0}.conf" - .format(wo_domain_name), - "/etc/nginx/conf.d/force-ssl-{0}.conf.disabled" - .format(wo_domain_name)) - Log.info(self, "Disabled HTTPS Force Redirection for Site " - " http://{0}".format(wo_domain_name)) - - -def archivedCertificateHandle(self, domain): - Log.warn( - self, "You already have an existing certificate " - "for the domain requested.\n" - "(ref: {0}/" - "{1}_ecc/{1}.conf)".format(WOVar.wo_ssl_archive, domain) + - "\nPlease select an option from below?" - "\n\t1: Reinstall existing certificate" - "\n\t2: Issue a new certificate to replace " - "the current one (limit ~5 per 7 days)" - "") - check_prompt = input( - "\nType the appropriate number [1-2] or any other key to cancel: ") - if not os.path.isfile("{0}/{1}/fullchain.pem" - .format(WOVar.wo_ssl_live, domain)): - Log.error( - self, "{0}/{1}/fullchain.pem file is missing." - .format(WOVar.wo_ssl_live, domain)) - - if check_prompt == "1": - Log.info(self, "Reinstalling SSL cert with acme.sh") - ssl = WOAcme.deploycert(self, domain) - if ssl: - - try: - - if not os.path.isfile("/var/www/{0}/conf/nginx/ssl.conf" - .format(domain)): - Log.info( - self, "Adding /var/www/{0}/conf/nginx/ssl.conf" - .format(domain)) - - sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" - .format(domain), - encoding='utf-8', mode='w') - sslconf.write("listen 443 ssl http2;\n" - "listen [::]:443 ssl http2;\n" - "ssl_certificate " - "{0}/{1}/fullchain.pem;\n" - "ssl_certificate_key {0}/{1}/key.pem;\n" - "ssl_trusted_certificate {0}/{1}/ca.pem;\n" - "ssl_stapling_verify on;\n" - .format(WOVar.wo_ssl_live, domain)) - sslconf.close() - - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "ssl.conf") - - elif (check_prompt == "2"): - Log.info(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--renew -d {0} --ecc " - "--force" - .format(domain)) - - if ssl: - - try: - - WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " - "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--install-cert -d {1} --ecc " - "--cert-file {0}/{1}/cert.pem " - "--key-file {0}/{1}/key.pem " - "--fullchain-file " - "{0}/{1}/fullchain.pem " - "ssl_trusted_certificate " - "{0}/{1}/ca.pem;\n" - "--reloadcmd " - "\"nginx -t && service nginx restart\" " - .format(WOVar.wo_ssl_live, domain)) - - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while installing " - "the certificate") - - else: - Log.error(self, "Operation cancelled by user.") - - if os.path.isfile("{0}/conf/nginx/ssl.conf" - .format(domain)): - Log.info(self, "Existing ssl.conf . Backing it up ..") - WOFileUtils.mvfile(self, "/var/www/{0}/conf/nginx/ssl.conf" - .format(domain), - '/var/www/{0}/conf/nginx/ssl.conf.bak' - .format(domain)) - - return ssl - - def setuprocketchat(self): if ((not WOVar.wo_platform_codename == 'bionic') and (not WOVar.wo_platform_codename == 'xenial')): @@ -1557,6 +1462,7 @@ def setuprocketchat(self): WOAptGet.install(self, ["snapd"]) if WOShellExec.cmd_exec(self, "snap install rocketchat-server"): return True + return False def setupngxblocker(self, domain, block=True): diff --git a/wo/cli/plugins/site_update.py b/wo/cli/plugins/site_update.py new file mode 100644 index 0000000..d602983 --- /dev/null +++ b/wo/cli/plugins/site_update.py @@ -0,0 +1,1098 @@ +""" wo site update """ +import json +import os + +from cement.core.controller import CementBaseController, expose + +from wo.cli.plugins.site_functions import * +from wo.cli.plugins.sitedb import (getAllsites, + getSiteInfo, updateSiteInfo) +from wo.core.acme import WOAcme +from wo.core.domainvalidate import WODomain +from wo.core.fileutils import WOFileUtils +from wo.core.git import WOGit +from wo.core.logging import Log +from wo.core.services import WOService +from wo.core.sslutils import SSL +from wo.core.variables import WOVar + + +class WOSiteUpdateController(CementBaseController): + class Meta: + label = 'update' + stacked_on = 'site' + stacked_type = 'nested' + description = ('This command updates websites configuration to ' + 'another as per the options are provided') + arguments = [ + (['site_name'], + dict(help='domain name for the site to be updated', + nargs='?')), + (['--password'], + dict(help="update to password for wordpress site user", + action='store_true')), + (['--html'], + dict(help="update to html site", action='store_true')), + (['--php'], + dict(help="update to php site", action='store_true')), + (['--php72'], + dict(help="update to php72 site", action='store_true')), + (['--php73'], + dict(help="update to php73 site", action='store_true')), + (['--php74'], + dict(help="update to php74 site", action='store_true')), + (['--mysql'], + dict(help="update to mysql site", action='store_true')), + (['--wp'], + dict(help="update to wordpress single site", + action='store_true')), + (['--wpsubdir'], + dict(help="update to wpsubdir site", action='store_true')), + (['--wpsubdomain'], + dict(help="update to wpsubdomain site", action='store_true')), + (['--wpfc'], + dict(help="update to wpfc cache", action='store_true')), + (['--wpsc'], + dict(help="update to wpsc cache", action='store_true')), + (['--wprocket'], + dict(help="update to WP-Rocket cache", action='store_true')), + (['--wpce'], + dict(help="update to Cache-Enabler cache", + action='store_true')), + (['--wpredis'], + dict(help="update to redis cache", action='store_true')), + (['-le', '--letsencrypt'], + dict(help="configure letsencrypt ssl for the site", + action='store' or 'store_const', + choices=('on', 'off', 'renew', 'subdomain', + 'wildcard', 'clean', 'purge'), + const='on', nargs='?')), + (['--force'], + dict(help="force LetsEncrypt certificate issuance/renewal", + action='store_true')), + (['--dns'], + dict(help="choose dns provider api for letsencrypt", + action='store' or 'store_const', + const='dns_cf', nargs='?')), + (['--dnsalias'], + dict(help="set domain used for acme dns alias validation", + action='store', nargs='?')), + (['--hsts'], + dict(help="configure hsts for the site", + action='store' or 'store_const', + choices=('on', 'off'), + const='on', nargs='?')), + (['--ngxblocker'], + dict(help="enable Ultimate Nginx bad bot blocker", + action='store' or 'store_const', + choices=('on', 'off'), + const='on', nargs='?')), + (['--proxy'], + dict(help="update to proxy site", nargs='+')), + (['--all'], + dict(help="update all sites", action='store_true')), + ] + + @expose(help="Update site type or cache") + def default(self): + pargs = self.app.pargs + + if pargs.all: + if pargs.site_name: + Log.error(self, "`--all` option cannot be used with site name" + " provided") + if pargs.html: + Log.error(self, "No site can be updated to html") + + if not (pargs.php or pargs.php72 or pargs.php73 or pargs.php74 or + pargs.mysql or pargs.wp or pargs.wpsubdir or + pargs.wpsubdomain or pargs.wpfc or pargs.wpsc or + pargs.wprocket or pargs.wpce or + pargs.wpredis or pargs.letsencrypt or pargs.hsts or + pargs.dns or pargs.force): + Log.error(self, "Please provide options to update sites.") + + if pargs.all: + if pargs.site_name: + Log.error(self, "`--all` option cannot be used with site name" + " provided") + + sites = getAllsites(self) + if not sites: + pass + else: + for site in sites: + pargs.site_name = site.sitename + Log.info(self, Log.ENDC + Log.BOLD + "Updating site {0}," + " please wait..." + .format(pargs.site_name)) + self.doupdatesite(pargs) + print("\n") + else: + self.doupdatesite(pargs) + + def doupdatesite(self, pargs): + pargs = self.app.pargs + letsencrypt = False + php73 = False + php74 = False + php72 = False + + data = dict() + try: + stype, cache = detSitePar(vars(pargs)) + except RuntimeError as e: + Log.debug(self, str(e)) + Log.error(self, "Please provide valid options combination for" + " site update") + + if stype is None and pargs.proxy: + stype, cache = 'proxy', '' + proxyinfo = pargs.proxy[0].strip() + if not proxyinfo: + Log.error(self, "Please provide proxy server host information") + proxyinfo = proxyinfo.split(':') + host = proxyinfo[0].strip() + port = '80' if len(proxyinfo) < 2 else proxyinfo[1].strip() + elif stype is None and not (pargs.proxy or pargs.letsencrypt): + stype, cache = 'html', 'basic' + elif stype and pargs.proxy: + Log.error(self, "--proxy can not be used with other site types") + + if not pargs.site_name: + try: + while not pargs.site_name: + pargs.site_name = (input('Enter site name : ').strip()) + except IOError: + Log.error(self, 'Unable to input site name, Please try again!') + + pargs.site_name = pargs.site_name.strip() + wo_domain = WODomain.validate(self, pargs.site_name) + wo_www_domain = "www.{0}".format(wo_domain) + (wo_domain_type, wo_root_domain) = WODomain.getlevel( + self, wo_domain) + wo_site_webroot = WOVar.wo_webroot + wo_domain + check_site = getSiteInfo(self, wo_domain) + + if check_site is None: + Log.error(self, " Site {0} does not exist.".format(wo_domain)) + else: + oldsitetype = check_site.site_type + oldcachetype = check_site.cache_type + check_ssl = check_site.is_ssl + check_php_version = check_site.php_version + + old_php72 = bool(check_php_version == "7.2") + old_php73 = bool(check_php_version == "7.3") + old_php74 = bool(check_php_version == "7.4") + + if ((pargs.password or pargs.hsts or + pargs.ngxblocker or pargs.letsencrypt == 'renew') and not ( + pargs.html or pargs.php or pargs.php72 or pargs.php73 or + pargs.php74 or + pargs.mysql or pargs.wp or pargs.wpfc or pargs.wpsc or + pargs.wprocket or pargs.wpce or + pargs.wpsubdir or pargs.wpsubdomain)): + + # update wordpress password + if (pargs.password): + try: + updatewpuserpassword(self, wo_domain, wo_site_webroot) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, "\nPassword Unchanged.") + return 0 + + # setup hsts + if (pargs.hsts): + if pargs.hsts == "on": + SSL.setuphsts(self, wo_domain, enable=True) + elif pargs.hsts == "off": + SSL.setuphsts(self, wo_domain, enable=False) + # Service Nginx Reload + if not WOService.reload_service(self, 'nginx'): + Log.error( + self, "service nginx reload failed. " + "check issues with `nginx -t` command") + return 0 + + # setup ngxblocker + if (pargs.ngxblocker): + if pargs.ngxblocker == "on": + if os.path.isdir('/etc/nginx/bots.d'): + try: + setupngxblocker(self, wo_domain) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, "\nngxblocker not enabled.") + else: + Log.error(self, 'ngxblocker stack is not installed') + elif pargs.ngxblocker == "off": + try: + setupngxblocker(self, wo_domain, False) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, "\nngxblocker not enabled.") + + # Service Nginx Reload + if not WOService.reload_service(self, 'nginx'): + Log.error(self, "service nginx reload failed. " + "check issues with `nginx -t` command") + return 0 + + # letsencryot rebew + if (pargs.letsencrypt == 'renew'): + if WOAcme.cert_check(self, wo_domain): + if not pargs.force: + if (SSL.getexpirationdays(self, wo_domain) > 30): + Log.error( + self, "Your cert will expire in more " + "than 30 days ( " + + str(SSL.getexpirationdays(self, wo_domain)) + + " days).\nAdd \'--force\' to force to renew") + Log.wait(self, "Renewing SSL certificate") + if WOAcme.renew(self, wo_domain): + Log.valide(self, "Renewing SSL certificate") + else: + Log.error(self, "Certificate doesn't exist") + return 0 + + if (((stype == 'php' and + oldsitetype not in ['html', 'proxy', 'php', 'php72', + 'php73', 'php74']) or + (stype == 'mysql' and oldsitetype not in [ + 'html', 'php', 'php72', 'php73', 'php74', 'proxy']) or + (stype == 'wp' and oldsitetype not in [ + 'html', 'php', 'php72', 'php73', 'php74', 'mysql', + 'proxy', 'wp']) or + (stype == 'wpsubdir' and oldsitetype in ['wpsubdomain']) or + (stype == 'wpsubdomain' and oldsitetype in ['wpsubdir']) or + (stype == oldsitetype and cache == oldcachetype)) and + not (pargs.php72 or pargs.php73 or pargs.php74)): + Log.info(self, Log.FAIL + "can not update {0} {1} to {2} {3}". + format(oldsitetype, oldcachetype, stype, cache)) + return 1 + + if stype == 'proxy': + data['site_name'] = wo_domain + data['www_domain'] = wo_www_domain + data['proxy'] = True + data['host'] = host + data['port'] = port + data['webroot'] = wo_site_webroot + data['currsitetype'] = oldsitetype + data['currcachetype'] = oldcachetype + + if stype == 'php': + data = dict( + site_name=wo_domain, www_domain=wo_www_domain, + static=False, basic=True, wp=False, wpfc=False, + php72=False, php73=False, php74=False, + wpsc=False, wpredis=False, wprocket=False, wpce=False, + multisite=False, wpsubdir=False, webroot=wo_site_webroot, + currsitetype=oldsitetype, currcachetype=oldcachetype) + + elif stype in ['mysql', 'wp', 'wpsubdir', 'wpsubdomain']: + data = dict( + site_name=wo_domain, www_domain=wo_www_domain, + static=False, basic=True, wp=False, wpfc=False, + wpsc=False, wpredis=False, wprocket=False, wpce=False, + multisite=False, wpsubdir=False, webroot=wo_site_webroot, + wo_db_name='', wo_db_user='', wo_db_pass='', + wo_db_host='', + currsitetype=oldsitetype, currcachetype=oldcachetype) + + if stype in ['wp', 'wpsubdir', 'wpsubdomain']: + data['wp'] = True + data['basic'] = False + data[cache] = True + if stype in ['wpsubdir', 'wpsubdomain']: + data['multisite'] = True + if stype == 'wpsubdir': + data['wpsubdir'] = True + + if ((pargs.php72 or pargs.php73 or + pargs.php74) and (not data)): + Log.debug( + self, "pargs php72, or php73, or php74 enabled") + data = dict( + site_name=wo_domain, + www_domain=wo_www_domain, + currsitetype=oldsitetype, + currcachetype=oldcachetype, + webroot=wo_site_webroot) + stype = oldsitetype + cache = oldcachetype + if oldsitetype == 'html' or oldsitetype == 'proxy': + data['static'] = False + data['wp'] = False + data['multisite'] = False + data['wpsubdir'] = False + elif (oldsitetype == 'php' or oldsitetype == 'mysql' or + oldsitetype == 'php73'or oldsitetype == 'php74'): + data['static'] = False + data['wp'] = False + data['multisite'] = False + data['wpsubdir'] = False + elif oldsitetype == 'wp': + data['static'] = False + data['wp'] = True + data['multisite'] = False + data['wpsubdir'] = False + elif oldsitetype == 'wpsubdir': + data['static'] = False + data['wp'] = True + data['multisite'] = True + data['wpsubdir'] = True + elif oldsitetype == 'wpsubdomain': + data['static'] = False + data['wp'] = True + data['multisite'] = True + data['wpsubdir'] = False + + if oldcachetype == 'basic': + data['basic'] = True + data['wpfc'] = False + data['wpsc'] = False + data['wpredis'] = False + data['wprocket'] = False + data['wpce'] = False + elif oldcachetype == 'wpfc': + data['basic'] = False + data['wpfc'] = True + data['wpsc'] = False + data['wpredis'] = False + data['wprocket'] = False + data['wpce'] = False + elif oldcachetype == 'wpsc': + data['basic'] = False + data['wpfc'] = False + data['wpsc'] = True + data['wpredis'] = False + data['wprocket'] = False + data['wpce'] = False + elif oldcachetype == 'wpredis': + data['basic'] = False + data['wpfc'] = False + data['wpsc'] = False + data['wpredis'] = True + data['wprocket'] = False + data['wpce'] = False + elif oldcachetype == 'wprocket': + data['basic'] = False + data['wpfc'] = False + data['wpsc'] = False + data['wpredis'] = False + data['wprocket'] = True + data['wpce'] = False + elif oldcachetype == 'wpce': + data['basic'] = False + data['wpfc'] = False + data['wpsc'] = False + data['wpredis'] = False + data['wprocket'] = False + data['wpce'] = True + + if pargs.php72: + Log.debug(self, "pargs.php72 detected") + data['php72'] = True + php72 = True + elif pargs.php73: + Log.debug(self, "pargs.php73 detected") + data['php73'] = True + php73 = True + elif pargs.php74: + Log.debug(self, "pargs.php74 detected") + data['php74'] = True + php74 = True + + if pargs.php72: + if php72 is old_php72: + Log.info(self, "PHP 7.2 is already enabled for given " + "site") + pargs.php72 = False + + if pargs.php73: + if php73 is old_php73: + Log.info(self, "PHP 7.3 is already enabled for given " + "site") + pargs.php73 = False + + if pargs.php74: + if php74 is old_php74: + Log.info(self, "PHP 7.4 is already enabled for given " + "site") + pargs.php74 = False + + if (data and (not pargs.php73) and + (not pargs.php74) and (not pargs.php72)): + data['php72'] = bool(old_php72 is True) + Log.debug(self, "data php72 = {0}".format(data['php72'])) + php72 = bool(old_php72 is True) + data['php73'] = bool(old_php73 is True) + Log.debug(self, "data php73 = {0}".format(data['php73'])) + php73 = bool(old_php73 is True) + data['php74'] = bool(old_php74 is True) + Log.debug(self, "data php74 = {0}".format(data['php74'])) + php74 = bool(old_php74 is True) + + if pargs.letsencrypt: + acme_domains = [] + acmedata = dict(acme_domains, dns=False, acme_dns='dns_cf', + dnsalias=False, acme_alias='', keylength='') + acmedata['keylength'] = self.app.config.get('letsencrypt', + 'keylength') + if pargs.letsencrypt == 'on': + data['letsencrypt'] = True + letsencrypt = True + acme_subdomain = bool(wo_domain_type == 'subdomain') + acme_wildcard = False + elif pargs.letsencrypt == 'subdomain': + data['letsencrypt'] = True + letsencrypt = True + acme_subdomain = True + acme_wildcard = False + elif pargs.letsencrypt == 'wildcard': + data['letsencrypt'] = True + letsencrypt = True + acme_wildcard = True + acme_subdomain = False + acmedata['dns'] = True + elif pargs.letsencrypt == 'off': + data['letsencrypt'] = False + letsencrypt = False + acme_subdomain = False + acme_wildcard = False + elif pargs.letsencrypt == 'clean': + data['letsencrypt'] = False + letsencrypt = False + acme_subdomain = False + acme_wildcard = False + elif pargs.letsencrypt == 'purge': + data['letsencrypt'] = False + letsencrypt = False + acme_subdomain = False + acme_wildcard = False + else: + data['letsencrypt'] = False + letsencrypt = False + acme_subdomain = False + acme_wildcard = False + + if not (acme_subdomain is True): + if letsencrypt is check_ssl: + if letsencrypt is False: + Log.error(self, "SSL is not configured for given " + "site") + elif letsencrypt is True: + Log.error(self, "SSL is already configured for given " + "site") + pargs.letsencrypt = False + + if pargs.all and pargs.letsencrypt == "off": + if letsencrypt is check_ssl: + if letsencrypt is False: + Log.error(self, "HTTPS is not configured for given " + "site", False) + return 0 + + if pargs.wpredis and data['currcachetype'] != 'wpredis': + data['wpredis'] = True + data['basic'] = False + cache = 'wpredis' + + if pargs.wprocket and data['currcachetype'] != 'wprocket': + data['wprocket'] = True + data['basic'] = False + cache = 'wprocket' + + if pargs.wpce and data['currcachetype'] != 'wpce': + data['wpce'] = True + data['basic'] = False + cache = 'wpce' + + if ((php73 is old_php73) and (php72 is old_php72) and + (php74 is old_php74) and (stype == oldsitetype and + cache == oldcachetype)): + Log.debug(self, "Nothing to update") + return 1 + + if php73 is True: + data['wo_php'] = 'php73' + check_php_version = '7.3' + elif php74 is True: + data['wo_php'] = 'php74' + check_php_version = '7.4' + elif php72 is True: + data['wo_php'] = 'php72' + check_php_version = '7.2' + else: + data['wo_php'] = 'php72' + check_php_version = '7.2' + + if pargs.hsts: + data['hsts'] = bool(pargs.hsts == "on") + + if pargs.ngxblocker: + ngxblocker = bool(pargs.ngxblocker == 'on') + + if not data: + Log.error(self, "Cannot update {0}, Invalid Options" + .format(wo_domain)) + + wo_auth = site_package_check(self, stype) + data['wo_db_name'] = check_site.db_name + data['wo_db_user'] = check_site.db_user + data['wo_db_pass'] = check_site.db_password + data['wo_db_host'] = check_site.db_host + + if not (pargs.letsencrypt or pargs.hsts or pargs.ngxblocker): + try: + pre_run_checks(self) + except SiteError as e: + Log.debug(self, str(e)) + Log.error(self, "NGINX configuration check failed.") + + try: + sitebackup(self, data) + except Exception as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Check the log for details: " + "`tail /var/log/wo/wordops.log` and please try again") + return 1 + + # setup NGINX configuration, and webroot + try: + setupdomain(self, data) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Update site failed." + "Check the log for details:" + "`tail /var/log/wo/wordops.log` and please try again") + return 1 + + if 'proxy' in data.keys() and data['proxy']: + updateSiteInfo(self, wo_domain, stype=stype, cache=cache, + ssl=(bool(check_site.is_ssl))) + Log.info(self, "Successfully updated site" + " http://{0}".format(wo_domain)) + return 0 + + if pargs.letsencrypt: + if data['letsencrypt'] is True: + # DNS API configuration + if pargs.dns: + Log.debug(self, "DNS validation enabled") + acmedata['dns'] = True + if not pargs.dns == 'dns_cf': + Log.debug(self, "DNS API : {0}".format(pargs.dns)) + acmedata['acme_dns'] = pargs.dns + if pargs.dnsalias: + Log.debug(self, "DNS Alias enabled") + acmedata['dnsalias'] = True + acmedata['acme_alias'] = pargs.dnsalias + # Set list of domains to secure + if acme_subdomain is True: + Log.info(self, "Certificate type : subdomain") + acme_domains = acme_domains + [ + '{0}'.format(wo_domain)] + elif acme_wildcard is True: + Log.info(self, "Certificate type : wildcard") + acme_domains = acme_domains + [ + '{0}'.format(wo_domain), + '*.{0}'.format(wo_domain)] + else: + Log.info(self, "Certificate type : domain") + acme_domains = acme_domains + [ + '{0}'.format(wo_domain), + 'www.{0}'.format(wo_domain)] + + if WOAcme.cert_check(self, wo_domain): + SSL.archivedcertificatehandle( + self, wo_domain, acme_domains) + else: + if acme_subdomain: + Log.debug(self, "checkWildcardExist on *.{0}" + .format(wo_root_domain)) + if SSL.checkwildcardexist(self, wo_root_domain): + Log.info( + self, "Using existing Wildcard SSL " + "certificate from {0} to secure {1}" + .format(wo_root_domain, wo_domain)) + Log.debug( + self, "symlink wildcard " + "cert between {0} & {1}" + .format(wo_domain, wo_root_domain)) + # copy the cert from the root domain + copyWildcardCert(self, wo_domain, + wo_root_domain) + else: + # check DNS records before issuing cert + if not acmedata['dns'] is True: + if not pargs.force: + if not WOAcme.check_dns(self, + acme_domains): + Log.error( + self, + "Aborting SSL certificate " + "issuance") + Log.debug( + self, "Setup Cert with acme.sh for {0}" + .format(wo_domain)) + if WOAcme.setupletsencrypt( + self, acme_domains, acmedata): + WOAcme.deploycert(self, wo_domain) + else: + Log.error( + self, "Unable to issue certificate") + else: + # check DNS records before issuing cert + if not acmedata['dns'] is True: + if not pargs.force: + if not WOAcme.check_dns(self, + acme_domains): + Log.error( + self, + "Aborting SSL " + "certificate issuance") + if WOAcme.setupletsencrypt( + self, acme_domains, acmedata): + WOAcme.deploycert(self, wo_domain) + else: + Log.error(self, "Unable to issue certificate") + + SSL.httpsredirect( + self, wo_domain, acme_domains, redirect=True) + SSL.siteurlhttps(self, wo_domain) + + if not WOService.reload_service(self, 'nginx'): + Log.error(self, "service nginx reload failed. " + "check issues with `nginx -t` command") + Log.info(self, "Congratulations! Successfully " + "Configured SSL on https://{0}".format(wo_domain)) + if (SSL.getexpirationdays(self, wo_domain) > 0): + Log.info(self, "Your cert will expire within " + + str(SSL.getexpirationdays(self, wo_domain)) + + " days.") + else: + Log.warn( + self, "Your cert already EXPIRED ! " + ".PLEASE renew soon . ") + + elif data['letsencrypt'] is False: + if pargs.letsencrypt == "off": + if os.path.islink("{0}/conf/nginx/ssl.conf" + .format(wo_site_webroot)): + WOFileUtils.remove_symlink(self, + "{0}/conf/nginx/ssl.conf" + .format(wo_site_webroot)) + elif os.path.isfile("{0}/conf/nginx/ssl.conf" + .format(wo_site_webroot)): + Log.info(self, 'Setting Nginx configuration') + WOFileUtils.mvfile(self, "{0}/conf/nginx/ssl.conf" + .format(wo_site_webroot), + '{0}/conf/nginx/ssl.conf.disabled' + .format(wo_site_webroot)) + SSL.httpsredirect( + self, wo_domain, acmedata, redirect=False) + if os.path.isfile("{0}/conf/nginx/hsts.conf" + .format(wo_site_webroot)): + WOFileUtils.mvfile(self, "{0}/conf/nginx/hsts.conf" + .format(wo_site_webroot), + '{0}/conf/nginx/' + 'hsts.conf.disabled' + .format(wo_site_webroot)) + # find all broken symlinks + sympath = "/var/www" + WOFileUtils.findBrokenSymlink(self, sympath) + + elif (pargs.letsencrypt == "clean" or + pargs.letsencrypt == "purge"): + WOAcme.removeconf(self, wo_domain) + # find all broken symlinks + sympath = "/var/www" + WOFileUtils.findBrokenSymlink(self, sympath) + if not WOService.reload_service(self, 'nginx'): + Log.error(self, "service nginx reload failed. " + "check issues with `nginx -t` command") + # Log.info(self,"Removing Cron Job set for cert + # auto-renewal") WOCron.remove_cron(self,'wo site + # update {0} --le=renew --min_expiry_limit 30 + # 2> \/dev\/null'.format(wo_domain)) + Log.info(self, "Successfully Disabled SSl for Site " + " http://{0}".format(wo_domain)) + + # Add nginx conf folder into GIT + WOGit.add(self, ["{0}/conf/nginx".format(wo_site_webroot)], + msg="Adding letsencrypts config of site: {0}" + .format(wo_domain)) + updateSiteInfo(self, wo_domain, ssl=letsencrypt) + return 0 + + if pargs.hsts: + if data['hsts'] is True: + if os.path.isfile(("{0}/conf/nginx/ssl.conf") + .format(wo_site_webroot)): + if not os.path.isfile("{0}/conf/nginx/hsts.conf" + .format(wo_site_webroot)): + SSL.setuphsts(self, wo_domain) + else: + Log.error(self, "HSTS is already configured for given " + "site") + if not WOService.reload_service(self, 'nginx'): + Log.error(self, "service nginx reload failed. " + "check issues with `nginx -t` command") + else: + Log.error(self, "HTTPS is not configured for given " + "site") + + elif data['hsts'] is False: + if os.path.isfile(("{0}/conf/nginx/hsts.conf") + .format(wo_site_webroot)): + WOFileUtils.mvfile(self, "{0}/conf/nginx/hsts.conf" + .format(wo_site_webroot), + '{0}/conf/nginx/hsts.conf.disabled' + .format(wo_site_webroot)) + if not WOService.reload_service(self, 'nginx'): + Log.error(self, "service nginx reload failed. " + "check issues with `nginx -t` command") + else: + Log.error(self, "HSTS is not configured for given " + "site") + if pargs.ngxblocker: + if ngxblocker is True: + setupngxblocker(self, wo_domain) + elif ngxblocker is False: + if os.path.isfile("{0}/conf/nginx/ngxblocker.conf" + .format(wo_site_webroot)): + WOFileUtils.mvfile( + self, + "{0}/conf/nginx/ngxblocker.conf" + .format(wo_site_webroot), + "{0}/conf/nginx/ngxblocker.conf.disabled" + .format(wo_site_webroot)) + # Service Nginx Reload + if not WOService.reload_service(self, 'nginx'): + Log.error(self, "service nginx reload failed. " + "check issues with `nginx -t` command") + + if stype == oldsitetype and cache == oldcachetype: + + # Service Nginx Reload + if not WOService.reload_service(self, 'nginx'): + Log.error(self, "service nginx reload failed. " + "check issues with `nginx -t` command") + + updateSiteInfo(self, wo_domain, stype=stype, cache=cache, + ssl=(bool(check_site.is_ssl)), + php_version=check_php_version) + + Log.info(self, "Successfully updated site" + " http://{0}".format(wo_domain)) + return 0 + + # if data['wo_db_name'] and not data['wp']: + if 'wo_db_name' in data.keys() and not data['wp']: + try: + data = setupdatabase(self, data) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Update site failed." + "Check the log for details:" + "`tail /var/log/wo/wordops.log` and please try again") + return 1 + try: + wodbconfig = open("{0}/wo-config.php".format(wo_site_webroot), + encoding='utf-8', mode='w') + wodbconfig.write("" + .format(data['wo_db_name'], + data['wo_db_user'], + data['wo_db_pass'], + data['wo_db_host'])) + wodbconfig.close() + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "creating wo-config.php failed.") + Log.info(self, Log.FAIL + "Update site failed. " + "Check the log for details: " + "`tail /var/log/wo/wordops.log` and please try again") + return 1 + + # Setup WordPress if old sites are html/php/mysql sites + if data['wp'] and oldsitetype in ['html', 'proxy', 'php', 'php72', + 'mysql', 'php73', 'php74']: + try: + wo_wp_creds = setupwordpress(self, data) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Update site failed." + "Check the log for details: " + "`tail /var/log/wo/wordops.log` and please try again") + return 1 + + # Uninstall unnecessary plugins + if oldsitetype in ['wp', 'wpsubdir', 'wpsubdomain']: + # Setup WordPress Network if update option is multisite + # and oldsite is WordPress single site + if data['multisite'] and oldsitetype == 'wp': + try: + setupwordpressnetwork(self, data) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Update site failed. " + "Check the log for details:" + " `tail /var/log/wo/wordops.log` " + "and please try again") + return 1 + + if ((oldcachetype in ['wpsc', 'basic', 'wpredis', 'wprocket', + 'wpce'] and + (data['wpfc'])) or (oldsitetype == 'wp' and + data['multisite'] and data['wpfc'])): + try: + plugin_data_object = { + "log_level": "INFO", + "log_filesize": 5, + "enable_purge": 1, + "enable_map": "0", + "enable_log": 0, + "enable_stamp": 1, + "purge_homepage_on_new": 1, + "purge_homepage_on_edit": 1, + "purge_homepage_on_del": 1, + "purge_archive_on_new": 1, + "purge_archive_on_edit": 0, + "purge_archive_on_del": 0, + "purge_archive_on_new_comment": 0, + "purge_archive_on_deleted_comment": 0, + "purge_page_on_mod": 1, + "purge_page_on_new_comment": 1, + "purge_page_on_deleted_comment": 1, + "cache_method": "enable_fastcgi", + "purge_method": "get_request", + "redis_hostname": "127.0.0.1", + "redis_port": "6379", + "redis_prefix": "nginx-cache:"} + plugin_data = json.dumps(plugin_data_object) + setupwp_plugin(self, 'nginx-helper', + 'rt_wp_nginx_helper_options', + plugin_data, data) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Update nginx-helper " + "settings failed. " + "Check the log for details:" + " `tail /var/log/wo/wordops.log` " + "and please try again") + return 1 + + elif ((oldcachetype in ['wpsc', 'basic', 'wpfc', + 'wprocket', 'wpce'] and + (data['wpredis'])) or (oldsitetype == 'wp' and + data['multisite'] and + data['wpredis'])): + try: + plugin_data_object = { + "log_level": "INFO", + "log_filesize": 5, + "enable_purge": 1, + "enable_map": "0", + "enable_log": 0, + "enable_stamp": 1, + "purge_homepage_on_new": 1, + "purge_homepage_on_edit": 1, + "purge_homepage_on_del": 1, + "purge_archive_on_new": 1, + "purge_archive_on_edit": 0, + "purge_archive_on_del": 0, + "purge_archive_on_new_comment": 0, + "purge_archive_on_deleted_comment": 0, + "purge_page_on_mod": 1, + "purge_page_on_new_comment": 1, + "purge_page_on_deleted_comment": 1, + "cache_method": "enable_redis", + "purge_method": "get_request", + "redis_hostname": "127.0.0.1", + "redis_port": "6379", + "redis_prefix": "nginx-cache:"} + plugin_data = json.dumps(plugin_data_object) + setupwp_plugin(self, 'nginx-helper', + 'rt_wp_nginx_helper_options', + plugin_data, data) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Update nginx-helper " + "settings failed. " + "Check the log for details:" + " `tail /var/log/wo/wordops.log` " + "and please try again") + return 1 + else: + try: + # disable nginx-helper + plugin_data_object = { + "log_level": "INFO", + "log_filesize": 5, + "enable_purge": 0, + "enable_map": 0, + "enable_log": 0, + "enable_stamp": 0, + "purge_homepage_on_new": 1, + "purge_homepage_on_edit": 1, + "purge_homepage_on_del": 1, + "purge_archive_on_new": 1, + "purge_archive_on_edit": 0, + "purge_archive_on_del": 0, + "purge_archive_on_new_comment": 0, + "purge_archive_on_deleted_comment": 0, + "purge_page_on_mod": 1, + "purge_page_on_new_comment": 1, + "purge_page_on_deleted_comment": 1, + "cache_method": "enable_redis", + "purge_method": "get_request", + "redis_hostname": "127.0.0.1", + "redis_port": "6379", + "redis_prefix": "nginx-cache:"} + plugin_data = json.dumps(plugin_data_object) + setupwp_plugin( + self, 'nginx-helper', + 'rt_wp_nginx_helper_options', plugin_data, data) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Update nginx-helper " + "settings failed. " + "Check the log for details:" + " `tail /var/log/wo/wordops.log` " + "and please try again") + return 1 + + if ((oldcachetype in ['wpsc', 'basic', + 'wpfc', 'wprocket', 'wpredis'] and + (data['wpce'])) or (oldsitetype == 'wp' and + data['multisite'] and + data['wpce'])): + try: + installwp_plugin(self, 'cache-enabler', data) + # setup cache-enabler + plugin_data_object = { + "expires": 24, + "new_post": 1, + "new_comment": 0, + "webp": 0, + "clear_on_upgrade": 1, + "compress": 0, + "excl_ids": "", + "excl_regexp": "", + "excl_cookies": "", + "incl_attributes": "", + "minify_html": 1} + plugin_data = json.dumps(plugin_data_object) + setupwp_plugin(self, 'cache-enabler', + 'cache-enabler', plugin_data, data) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Update cache-enabler " + "settings failed. " + "Check the log for details:" + " `tail /var/log/wo/wordops.log` " + "and please try again") + return 1 + + if oldcachetype == 'wpsc' and not data['wpsc']: + try: + uninstallwp_plugin(self, 'wp-super-cache', data) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Update site failed." + "Check the log for details:" + " `tail /var/log/wo/wordops.log` " + "and please try again") + return 1 + + if oldcachetype == 'wpredis' and not data['wpredis']: + try: + uninstallwp_plugin(self, 'redis-cache', data) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Update site failed." + "Check the log for details:" + " `tail /var/log/wo/wordops.log` " + "and please try again") + return 1 + + if oldcachetype != 'wpsc' and data['wpsc']: + try: + installwp_plugin(self, 'wp-super-cache', data) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Update site failed." + "Check the log for details: " + "`tail /var/log/wo/wordops.log` and please try again") + return 1 + + if oldcachetype == 'wprocket' and not data['wprocket']: + try: + uninstallwp_plugin(self, 'wp-rocket', data) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Update site failed." + "Check the log for details: " + "`tail /var/log/wo/wordops.log` and please try again") + return 1 + + if oldcachetype == 'wpce' and not data['wpce']: + try: + uninstallwp_plugin(self, 'cache-enabler', data) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Update site failed." + "Check the log for details: " + "`tail /var/log/wo/wordops.log` and please try again") + return 1 + + # Service Nginx Reload + if not WOService.reload_service(self, 'nginx'): + Log.error(self, "service nginx reload failed. " + "check issues with `nginx -t` command") + + WOGit.add(self, ["/etc/nginx"], + msg="{0} updated with {1} {2}" + .format(wo_www_domain, stype, cache)) + # Setup Permissions for webroot + try: + setwebrootpermissions(self, data['webroot']) + except SiteError as e: + Log.debug(self, str(e)) + Log.info(self, Log.FAIL + "Update site failed." + "Check the log for details: " + "`tail /var/log/wo/wordops.log` and please try again") + return 1 + + if wo_auth and len(wo_auth): + for msg in wo_auth: + Log.info(self, Log.ENDC + msg) + + display_cache_settings(self, data) + if data['wp'] and oldsitetype in ['html', 'php', 'mysql']: + Log.info(self, "\n\n" + Log.ENDC + "WordPress admin user :" + " {0}".format(wo_wp_creds['wp_user'])) + Log.info(self, Log.ENDC + "WordPress admin password : {0}" + .format(wo_wp_creds['wp_pass']) + "\n\n") + if oldsitetype in ['html', 'php'] and stype != 'php': + updateSiteInfo(self, wo_domain, stype=stype, cache=cache, + db_name=data['wo_db_name'], + db_user=data['wo_db_user'], + db_password=data['wo_db_pass'], + db_host=data['wo_db_host'], + ssl=bool(check_site.is_ssl), + php_version=check_php_version) + else: + updateSiteInfo(self, wo_domain, stype=stype, cache=cache, + ssl=bool(check_site.is_ssl), + php_version=check_php_version) + Log.info(self, "Successfully updated site" + " http://{0}".format(wo_domain)) + return 0 diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index 797fae5..46a2643 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -41,8 +41,12 @@ class WOStackController(CementBaseController): dict(help='Install Nginx stack', action='store_true')), (['--php'], dict(help='Install PHP 7.2 stack', action='store_true')), + (['--php72'], + dict(help='Install PHP 7.2 stack', action='store_true')), (['--php73'], dict(help='Install PHP 7.3 stack', action='store_true')), + (['--php74'], + dict(help='Install PHP 7.4 stack', action='store_true')), (['--mysql'], dict(help='Install MySQL stack', action='store_true')), (['--mysqlclient'], @@ -105,40 +109,40 @@ class WOStackController(CementBaseController): def install(self, packages=[], apt_packages=[], disp_msg=True): """Start installation of packages""" self.msg = [] - empty_packages = [] - wo_webroot = "/var/www/" pargs = self.app.pargs try: # Default action for stack installation - if ((not pargs.web) and (not pargs.admin) and - (not pargs.nginx) and (not pargs.php) and - (not pargs.mysql) and (not pargs.wpcli) and - (not pargs.phpmyadmin) and (not pargs.composer) and - (not pargs.netdata) and (not pargs.dashboard) and - (not pargs.fail2ban) and (not pargs.security) and - (not pargs.mysqlclient) and (not pargs.mysqltuner) and - (not pargs.adminer) and (not pargs.utils) and - (not pargs.redis) and (not pargs.proftpd) and - (not pargs.extplorer) and (not pargs.clamav) and - (not pargs.cheat) and (not pargs.nanorc) and - (not pargs.ufw) and (not pargs.ngxblocker) and - (not pargs.phpredisadmin) and (not pargs.sendmail) and - (not pargs.php73) and (not pargs.all)): + if not (pargs.web or pargs.admin or pargs.nginx or + pargs.php or pargs.php72 or pargs.php73 or pargs.php74 or + pargs.mysql or pargs.wpcli or pargs.phpmyadmin or + pargs.composer or pargs.netdata or pargs.composer or + pargs.dashboard or pargs.fail2ban or pargs.security or + pargs.mysqlclient or pargs.mysqltuner or + pargs.admin or pargs.adminer or + pargs.utils or pargs.redis or + pargs.proftpd or pargs.extplorer or + pargs.clamav or pargs.cheat or pargs.nanorc or + pargs.ufw or pargs.ngxblocker or + pargs.phpredisadmin or pargs.sendmail or pargs.all): pargs.web = True pargs.admin = True pargs.fail2ban = True + if pargs.php: + pargs.php72 = True + if pargs.all: pargs.web = True pargs.admin = True pargs.php73 = True + pargs.php74 = True pargs.redis = True pargs.proftpd = True if pargs.web: pargs.nginx = True - pargs.php = True + pargs.php72 = True pargs.mysql = True pargs.wpcli = True pargs.sendmail = True @@ -163,23 +167,8 @@ class WOStackController(CementBaseController): # Nginx if pargs.nginx: Log.debug(self, "Setting apt_packages variable for Nginx") - if not (WOAptGet.is_installed(self, 'nginx-custom')): - if not (WOAptGet.is_installed(self, 'nginx-plus') or - WOAptGet.is_installed(self, 'nginx')): - if not os.path.isfile('/usr/sbin/nginx'): - apt_packages = apt_packages + WOVar.wo_nginx - else: - if WOAptGet.is_installed(self, 'nginx-plus'): - Log.info(self, "NGINX PLUS Detected ...") - apt = ["nginx-plus"] + WOVar.wo_nginx - post_pref(self, apt, empty_packages) - elif WOAptGet.is_installed(self, 'nginx'): - Log.info(self, "WordOps detected an already " - "installed nginx package." - "It may or may not have " - "required modules.\n") - apt = ["nginx"] + WOVar.wo_nginx - post_pref(self, apt, empty_packages) + if not WOAptGet.is_exec(self, 'nginx'): + apt_packages = apt_packages + WOVar.wo_nginx else: Log.debug(self, "Nginx already installed") @@ -192,11 +181,13 @@ class WOStackController(CementBaseController): Log.info(self, "Redis already installed") # PHP 7.2 - if pargs.php: + if pargs.php72: Log.debug(self, "Setting apt_packages variable for PHP 7.2") if not (WOAptGet.is_installed(self, 'php7.2-fpm')): - apt_packages = (apt_packages + WOVar.wo_php + - WOVar.wo_php_extra) + apt_packages = apt_packages + WOVar.wo_php72 + if not (WOAptGet.is_installed(self, 'php7.3-fpm') or + WOAptGet.is_installed(self, 'php7.4-fpm')): + apt_packages = apt_packages + WOVar.wo_php_extra else: Log.debug(self, "PHP 7.2 already installed") Log.info(self, "PHP 7.2 already installed") @@ -205,13 +196,26 @@ class WOStackController(CementBaseController): if pargs.php73: Log.debug(self, "Setting apt_packages variable for PHP 7.3") if not WOAptGet.is_installed(self, 'php7.3-fpm'): - apt_packages = (apt_packages + WOVar.wo_php + - WOVar.wo_php73 + - WOVar.wo_php_extra) + apt_packages = apt_packages + WOVar.wo_php73 + if not (WOAptGet.is_installed(self, 'php7.2-fpm') or + WOAptGet.is_installed(self, 'php7.4-fpm')): + apt_packages = apt_packages + WOVar.wo_php_extra else: Log.debug(self, "PHP 7.3 already installed") Log.info(self, "PHP 7.3 already installed") + # PHP 7.4 + if pargs.php74: + Log.debug(self, "Setting apt_packages variable for PHP 7.4") + if not WOAptGet.is_installed(self, 'php7.4-fpm'): + apt_packages = apt_packages + WOVar.wo_php74 + if not (WOAptGet.is_installed(self, 'php7.3-fpm') or + WOAptGet.is_installed(self, 'php7.2-fpm')): + apt_packages = apt_packages + WOVar.wo_php_extra + else: + Log.debug(self, "PHP 7.4 already installed") + Log.info(self, "PHP 7.4 already installed") + # MariaDB 10.3 if pargs.mysql: pargs.mysqltuner = True @@ -235,8 +239,7 @@ class WOStackController(CementBaseController): # WP-CLI if pargs.wpcli: Log.debug(self, "Setting packages variable for WP-CLI") - if ((not os.path.isfile("/usr/local/bin/wp")) and - (not os.path.isfile("/usr/bin/wp"))): + if not WOAptGet.is_exec(self, 'wp'): packages = packages + [["https://github.com/wp-cli/wp-cli/" "releases/download/v{0}/" "wp-cli-{0}.phar" @@ -328,9 +331,9 @@ class WOStackController(CementBaseController): # Composer if pargs.composer: - if not WOShellExec.cmd_exec(self, 'php -v'): + if not WOAptGet.is_exec(self, 'php'): pargs.php = True - if not os.path.isfile('/usr/local/bin/composer'): + if not WOAptGet.is_exec(self, 'composer'): Log.debug(self, "Setting packages variable for Composer ") packages = packages + [["https://getcomposer.org/" "installer", @@ -344,7 +347,7 @@ class WOStackController(CementBaseController): if pargs.adminer: if not os.path.isfile("{0}22222/htdocs/db/" "adminer/index.php" - .format(wo_webroot)): + .format(WOVar.wo_webroot)): Log.debug(self, "Setting packages variable for Adminer ") packages = packages + [[ "https://github.com/vrana/adminer/" @@ -434,6 +437,8 @@ class WOStackController(CementBaseController): # ultimate ngx_blocker if pargs.ngxblocker: + if not WOAptGet.is_exec(self, 'nginx'): + pargs.nginx = True if not os.path.isdir('/etc/nginx/bots.d'): Log.debug(self, "Setting packages variable for ngxblocker") packages = packages + \ @@ -468,6 +473,12 @@ class WOStackController(CementBaseController): # UTILS if pargs.utils: + if not WOShellExec.cmd_exec(self, 'mysqladmin ping'): + pargs.mysql = True + if not (WOAptGet.is_installed(self, 'php7.2-fpm') or + WOAptGet.is_installed(self, 'php7.3-fpm') or + WOAptGet.is_installed(self, 'php7.4-fpm')): + pargs.php = True Log.debug(self, "Setting packages variable for utils") packages = packages + [[ "https://raw.githubusercontent.com" @@ -524,9 +535,7 @@ class WOStackController(CementBaseController): Log.wait(self, "Installing APT packages ") WOAptGet.install(self, apt_packages) Log.valide(self, "Installing APT packages ") - Log.wait(self, "Configuring APT packages ") post_pref(self, apt_packages, []) - Log.valide(self, "Configuring APT packages ") if (packages): Log.debug(self, "Downloading following: {0}".format(packages)) WODownload.download(self, packages) @@ -563,13 +572,18 @@ class WOStackController(CementBaseController): (not pargs.cheat) and (not pargs.nanorc) and (not pargs.ufw) and (not pargs.ngxblocker) and (not pargs.phpredisadmin) and (not pargs.sendmail) and - (not pargs.php73) and (not pargs.all)): + (not pargs.php73) and (not pargs.php74) and + (not pargs.php72) and (not pargs.all)): self.app.args.print_help() + if pargs.php: + pargs.php72 = True + if pargs.all: pargs.web = True pargs.admin = True pargs.php73 = True + pargs.php74 = True pargs.fail2ban = True pargs.proftpd = True pargs.utils = True @@ -580,7 +594,7 @@ class WOStackController(CementBaseController): if pargs.web: pargs.nginx = True - pargs.php = True + pargs.php72 = True pargs.mysql = True pargs.wpcli = True pargs.sendmail = True @@ -605,24 +619,40 @@ class WOStackController(CementBaseController): apt_packages = apt_packages + WOVar.wo_nginx # PHP 7.2 - if pargs.php: - Log.debug(self, "Removing apt_packages variable of PHP") - if WOAptGet.is_installed(self, 'php7.2-fpm'): - if not WOAptGet.is_installed(self, 'php7.3-fpm'): - apt_packages = apt_packages + WOVar.wo_php + \ - WOVar.wo_php_extra - else: - apt_packages = apt_packages + WOVar.wo_php + if pargs.php72: + Log.debug(self, "Setting apt_packages variable for PHP 7.2") + if (WOAptGet.is_installed(self, 'php7.2-fpm')): + apt_packages = apt_packages + WOVar.wo_php72 + if not (WOAptGet.is_installed(self, 'php7.3-fpm') or + WOAptGet.is_installed(self, 'php7.4-fpm')): + apt_packages = apt_packages + WOVar.wo_php_extra + else: + Log.debug(self, "PHP 7.2 is not installed") + Log.info(self, "PHP 7.2 is not installed") - # PHP7.3 + # PHP 7.3 if pargs.php73: - Log.debug(self, "Removing apt_packages variable of PHP 7.3") + Log.debug(self, "Setting apt_packages variable for PHP 7.3") if WOAptGet.is_installed(self, 'php7.3-fpm'): - if not (WOAptGet.is_installed(self, 'php7.2-fpm')): - apt_packages = apt_packages + WOVar.wo_php73 + \ - WOVar.wo_php_extra - else: - apt_packages = apt_packages + WOVar.wo_php73 + apt_packages = apt_packages + WOVar.wo_php73 + if not (WOAptGet.is_installed(self, 'php7.2-fpm') or + WOAptGet.is_installed(self, 'php7.4-fpm')): + apt_packages = apt_packages + WOVar.wo_php_extra + else: + Log.debug(self, "PHP 7.3 is not installed") + Log.info(self, "PHP 7.3 is not installed") + + # PHP 7.4 + if pargs.php74: + Log.debug(self, "Setting apt_packages variable for PHP 7.4") + if WOAptGet.is_installed(self, 'php7.4-fpm'): + apt_packages = apt_packages + WOVar.wo_php74 + if not (WOAptGet.is_installed(self, 'php7.3-fpm') or + WOAptGet.is_installed(self, 'php7.2-fpm')): + apt_packages = apt_packages + WOVar.wo_php_extra + else: + Log.debug(self, "PHP 7.4 is not installed") + Log.info(self, "PHP 7.4 is not installed") # REDIS if pargs.redis: @@ -634,9 +664,7 @@ class WOStackController(CementBaseController): if pargs.mysql: if WOAptGet.is_installed(self, 'mariadb-server'): Log.debug(self, "Removing apt_packages variable of MySQL") - apt_packages = apt_packages + ['mariadb-server', - 'mysql-common', - 'mariadb-client'] + apt_packages = apt_packages + WOVar.wo_mysql # mysqlclient if pargs.mysqlclient: @@ -849,13 +877,18 @@ class WOStackController(CementBaseController): (not pargs.cheat) and (not pargs.nanorc) and (not pargs.ufw) and (not pargs.ngxblocker) and (not pargs.phpredisadmin) and (not pargs.sendmail) and - (not pargs.php73) and (not pargs.all)): + (not pargs.php73) and (not pargs.php74) and + (not pargs.php72) and (not pargs.all)): self.app.args.print_help() + if pargs.php: + pargs.php72 = True + if pargs.all: pargs.web = True pargs.admin = True pargs.php73 = True + pargs.php74 = True pargs.fail2ban = True pargs.proftpd = True pargs.utils = True @@ -864,7 +897,7 @@ class WOStackController(CementBaseController): if pargs.web: pargs.nginx = True - pargs.php = True + pargs.php72 = True pargs.mysql = True pargs.wpcli = True pargs.sendmail = True @@ -875,6 +908,7 @@ class WOStackController(CementBaseController): pargs.netdata = True pargs.mysqltuner = True pargs.cheat = True + packages = packages + ['/var/www/22222/htdocs'] if pargs.security: pargs.fail2ban = True @@ -890,25 +924,41 @@ class WOStackController(CementBaseController): else: Log.info(self, "Nginx is not installed") - # PHP - if pargs.php: - Log.debug(self, "Add PHP to apt_packages list") - if WOAptGet.is_installed(self, 'php7.2-fpm'): - if not (WOAptGet.is_installed(self, 'php7.3-fpm')): - apt_packages = apt_packages + WOVar.wo_php + \ - WOVar.wo_php_extra - else: - apt_packages = apt_packages + WOVar.wo_php + # PHP 7.2 + if pargs.php72: + Log.debug(self, "Setting apt_packages variable for PHP 7.2") + if (WOAptGet.is_installed(self, 'php7.2-fpm')): + apt_packages = apt_packages + WOVar.wo_php72 + if not (WOAptGet.is_installed(self, 'php7.3-fpm') or + WOAptGet.is_installed(self, 'php7.4-fpm')): + apt_packages = apt_packages + WOVar.wo_php_extra + else: + Log.debug(self, "PHP 7.2 is not installed") + Log.info(self, "PHP 7.2 is not installed") # PHP 7.3 if pargs.php73: - Log.debug(self, "Removing apt_packages variable of PHP 7.3") + Log.debug(self, "Setting apt_packages variable for PHP 7.3") if WOAptGet.is_installed(self, 'php7.3-fpm'): - if not (WOAptGet.is_installed(self, 'php7.2-fpm')): - apt_packages = apt_packages + WOVar.wo_php73 + \ - WOVar.wo_php_extra - else: - apt_packages = apt_packages + WOVar.wo_php73 + apt_packages = apt_packages + WOVar.wo_php73 + if not (WOAptGet.is_installed(self, 'php7.2-fpm') or + WOAptGet.is_installed(self, 'php7.4-fpm')): + apt_packages = apt_packages + WOVar.wo_php_extra + else: + Log.debug(self, "PHP 7.3 is not installed") + Log.info(self, "PHP 7.3 is not installed") + + # PHP 7.4 + if pargs.php74: + Log.debug(self, "Setting apt_packages variable for PHP 7.4") + if WOAptGet.is_installed(self, 'php7.4-fpm'): + apt_packages = apt_packages + WOVar.wo_php74 + if not (WOAptGet.is_installed(self, 'php7.3-fpm') or + WOAptGet.is_installed(self, 'php7.2-fpm')): + apt_packages = apt_packages + WOVar.wo_php_extra + else: + Log.debug(self, "PHP 7.4 is not installed") + Log.info(self, "PHP 7.4 is not installed") # REDIS if pargs.redis: diff --git a/wo/cli/plugins/stack_pref.py b/wo/cli/plugins/stack_pref.py index a387cea..d3d2151 100644 --- a/wo/cli/plugins/stack_pref.py +++ b/wo/cli/plugins/stack_pref.py @@ -20,6 +20,7 @@ from wo.core.shellexec import CommandExecutionError, WOShellExec from wo.core.sslutils import SSL from wo.core.template import WOTemplate from wo.core.variables import WOVar +from wo.core.stackconf import WOConf def pre_pref(self, apt_packages): @@ -112,8 +113,8 @@ def pre_pref(self, apt_packages): WORepo.add_key(self, WOVar.wo_nginx_key) # add php repository - if (set(WOVar.wo_php73).issubset(set(apt_packages)) or - set(WOVar.wo_php).issubset(set(apt_packages))): + if (('php7.3-fpm' in apt_packages) or + ('php7.2-fpm' in apt_packages) or ('php7.4-fpm' in apt_packages)): if (WOVar.wo_distro == 'ubuntu'): Log.debug(self, 'Adding ppa for PHP') if not os.path.isfile( @@ -182,13 +183,13 @@ def post_pref(self, apt_packages, packages, upgrade=False): ngxcom = '/etc/nginx/common' ngxroot = '/var/www/' WOGit.add(self, ["/etc/nginx"], msg="Adding Nginx into Git") - data = dict(tls13=True) + data = dict(tls13=True, release=WOVar.wo_version) WOTemplate.deploy(self, '/etc/nginx/nginx.conf', 'nginx-core.mustache', data) if not os.path.isfile('{0}/gzip.conf.disabled'.format(ngxcnf)): - data = dict() + data = dict(release=WOVar.wo_version) WOTemplate.deploy(self, '{0}/gzip.conf'.format(ngxcnf), 'gzip.mustache', data) @@ -210,17 +211,19 @@ def post_pref(self, apt_packages, packages, upgrade=False): '\t$request_filename;\n') try: data = dict(php="9000", debug="9001", - php7="9070", debug7="9170") + php7="9070", debug7="9170", + release=WOVar.wo_version) WOTemplate.deploy( self, '{0}/upstream.conf'.format(ngxcnf), 'upstream.mustache', data, overwrite=True) data = dict(phpconf=( - bool(WOAptGet.is_installed(self, 'php7.2-fpm')))) + bool(WOAptGet.is_installed(self, 'php7.2-fpm'))), + release=WOVar.wo_version) WOTemplate.deploy( self, '{0}/stub_status.conf'.format(ngxcnf), 'stub_status.mustache', data) - data = dict() + data = dict(release=WOVar.wo_version) WOTemplate.deploy( self, '{0}/webp.conf'.format(ngxcnf), 'webp.mustache', data, overwrite=False) @@ -243,7 +246,7 @@ def post_pref(self, apt_packages, packages, upgrade=False): os.makedirs('/etc/nginx/common') try: - data = dict() + data = dict(release=WOVar.wo_version) # Common Configuration WOTemplate.deploy(self, @@ -255,89 +258,52 @@ def post_pref(self, apt_packages, packages, upgrade=False): '{0}/wpsubdir.conf' .format(ngxcom), 'wpsubdir.mustache', data) - data = dict(upstream="php72") - # PHP 7.2 conf - WOTemplate.deploy(self, - '{0}/php72.conf' - .format(ngxcom), - 'php.mustache', data) - WOTemplate.deploy(self, - '{0}/redis-php72.conf' - .format(ngxcom), - 'redis.mustache', data) + wo_php_version = ["php72", "php73", "php74"] + for wo_php in wo_php_version: + data = dict(upstream="{0}".format(wo_php), + release=WOVar.wo_version) + WOTemplate.deploy(self, + '{0}/{1}.conf' + .format(ngxcom, wo_php), + 'php.mustache', data) - WOTemplate.deploy(self, - '{0}/wpcommon-php72.conf' - .format(ngxcom), - 'wpcommon.mustache', data) + WOTemplate.deploy( + self, '{0}/redis-{1}.conf'.format(ngxcom, wo_php), + 'redis.mustache', data) - WOTemplate.deploy(self, - '{0}/wpfc-php72.conf' - .format(ngxcom), - 'wpfc.mustache', data) - WOTemplate.deploy(self, - '{0}/wpsc-php72.conf' - .format(ngxcom), - 'wpsc.mustache', data) + WOTemplate.deploy( + self, '{0}/wpcommon-{1}.conf'.format(ngxcom, wo_php), + 'wpcommon.mustache', data) - WOTemplate.deploy(self, - '{0}/wprocket-php72.conf' - .format(ngxcom), - 'wprocket.mustache', data) + WOTemplate.deploy( + self, '{0}/wpfc-{1}.conf'.format(ngxcom, wo_php), + 'wpfc.mustache', data) - WOTemplate.deploy(self, - '{0}/wpce-php72.conf' - .format(ngxcom), - 'wpce.mustache', data) - # PHP 7.3 conf - data = dict(upstream="php73") + WOTemplate.deploy( + self, '{0}/wpsc-{1}.conf'.format(ngxcom, wo_php), + 'wpsc.mustache', data) - WOTemplate.deploy(self, - '{0}/php73.conf' - .format(ngxcom), - 'php.mustache', data) + WOTemplate.deploy( + self, '{0}/wprocket-{1}.conf'.format(ngxcom, wo_php), + 'wprocket.mustache', data) - WOTemplate.deploy(self, - '{0}/redis-php73.conf' - .format(ngxcom), - 'redis.mustache', data) + WOTemplate.deploy( + self, '{0}/wpce-{1}.conf'.format(ngxcom, wo_php), + 'wpce.mustache', data) - WOTemplate.deploy(self, - '{0}/wpcommon-php73.conf' - .format(ngxcom), - 'wpcommon.mustache', data) - - WOTemplate.deploy(self, - '{0}/wpfc-php73.conf' - .format(ngxcom), - 'wpfc.mustache', data) - WOTemplate.deploy(self, - '{0}/wpsc-php73.conf' - .format(ngxcom), - 'wpsc.mustache', data) - - WOTemplate.deploy(self, - '{0}/wprocket-php73.conf' - .format(ngxcom), - 'wprocket.mustache', data) - - WOTemplate.deploy(self, - '{0}/wpce-php73.conf' - .format(ngxcom), - 'wpce.mustache', data) except CommandExecutionError as e: Log.debug(self, "{0}".format(e)) with open("/etc/nginx/common/release", - "w") as release_file: + "w", encoding='utf-8') as release_file: release_file.write("v{0}" .format(WOVar.wo_version)) release_file.close() # Following files should not be overwrited - data = dict(webroot=ngxroot) + data = dict(webroot=ngxroot, release=WOVar.wo_version) WOTemplate.deploy(self, '{0}/acl.conf' .format(ngxcom), @@ -383,7 +349,7 @@ def post_pref(self, apt_packages, packages, upgrade=False): os.makedirs('/etc/nginx/sites-enabled') # 22222 port settings - data = dict(webroot=ngxroot) + data = dict(webroot=ngxroot, release=WOVar.wo_version) WOTemplate.deploy( self, '/etc/nginx/sites-available/22222', @@ -488,7 +454,7 @@ def post_pref(self, apt_packages, packages, upgrade=False): WOVar.wo_fqdn)]) if not os.path.isfile("/opt/cf-update.sh"): - data = dict() + data = dict(release=WOVar.wo_version) WOTemplate.deploy(self, '/opt/cf-update.sh', 'cf-update.mustache', data, overwrite=False) @@ -528,10 +494,11 @@ def post_pref(self, apt_packages, packages, upgrade=False): WOShellExec.cmd_exec(self, 'systemctl daemon-reload') WOService.restart_service(self, 'nginx') - if set(WOVar.wo_php).issubset(set(apt_packages)): + if set(WOVar.wo_php72).issubset(set(apt_packages)): WOGit.add(self, ["/etc/php"], msg="Adding PHP into Git") Log.info(self, "Configuring php7.2-fpm") ngxroot = '/var/www/' + # Create log directories if not os.path.exists('/var/log/php/7.2/'): Log.debug(self, 'Creating directory /var/log/php/7.2/') @@ -800,6 +767,153 @@ def post_pref(self, apt_packages, packages, upgrade=False): else: WOGit.add(self, ["/etc/php"], msg="Adding PHP into Git") + # PHP7.4 configuration + # php7.4 configuration + if set(WOVar.wo_php74).issubset(set(apt_packages)): + WOGit.add(self, ["/etc/php"], msg="Adding PHP into Git") + Log.info(self, "Configuring php7.4-fpm") + ngxroot = '/var/www/' + # Create log directories + if not os.path.exists('/var/log/php/7.4/'): + Log.debug(self, 'Creating directory /var/log/php/7.4/') + os.makedirs('/var/log/php/7.4/') + + if not os.path.isfile('/etc/php/7.4/fpm/php.ini.orig'): + WOFileUtils.copyfile(self, '/etc/php/7.4/fpm/php.ini', + '/etc/php/7.4/fpm/php.ini.orig') + + # Parse etc/php/7.4/fpm/php.ini + config = configparser.ConfigParser() + Log.debug(self, "configuring php file /etc/php/7.4/" + "fpm/php.ini") + config.read('/etc/php/7.4/fpm/php.ini.orig') + config['PHP']['expose_php'] = 'Off' + config['PHP']['post_max_size'] = '100M' + config['PHP']['upload_max_filesize'] = '100M' + config['PHP']['max_execution_time'] = '300' + config['PHP']['max_input_time'] = '300' + config['PHP']['max_input_vars'] = '20000' + config['Date']['date.timezone'] = WOVar.wo_timezone + config['opcache']['opcache.enable'] = '1' + config['opcache']['opcache.interned_strings_buffer'] = '8' + config['opcache']['opcache.max_accelerated_files'] = '10000' + config['opcache']['opcache.memory_consumption'] = '256' + config['opcache']['opcache.save_comments'] = '1' + config['opcache']['opcache.revalidate_freq'] = '5' + config['opcache']['opcache.consistency_checks'] = '0' + config['opcache']['opcache.validate_timestamps'] = '1' + with open('/etc/php/7.4/fpm/php.ini', + encoding='utf-8', mode='w') as configfile: + Log.debug(self, "Writting php configuration into " + "/etc/php/7.4/fpm/php.ini") + config.write(configfile) + + # Render php-fpm pool template for php7.4 + data = dict(pid="/run/php/php7.4-fpm.pid", + error_log="/var/log/php7.4-fpm.log", + include="/etc/php/7.4/fpm/pool.d/*.conf") + WOTemplate.deploy( + self, '/etc/php/7.4/fpm/php-fpm.conf', + 'php-fpm.mustache', data) + + data = dict(pool='www-php74', listen='php74-fpm.sock', + user='www-data', + group='www-data', listenuser='root', + listengroup='www-data', openbasedir=True) + WOTemplate.deploy(self, '/etc/php/7.4/fpm/pool.d/www.conf', + 'php-pool.mustache', data) + data = dict(pool='www-two-php74', listen='php74-two-fpm.sock', + user='www-data', + group='www-data', listenuser='root', + listengroup='www-data', openbasedir=True) + WOTemplate.deploy(self, '/etc/php/7.4/fpm/pool.d/www-two.conf', + 'php-pool.mustache', data) + + # Generate /etc/php/7.4/fpm/pool.d/debug.conf + WOFileUtils.copyfile(self, "/etc/php/7.4/fpm/pool.d/www.conf", + "/etc/php/7.4/fpm/pool.d/debug.conf") + WOFileUtils.searchreplace(self, "/etc/php/7.4/fpm/pool.d/" + "debug.conf", "[www-php74]", "[debug]") + config = configparser.ConfigParser() + config.read('/etc/php/7.4/fpm/pool.d/debug.conf') + config['debug']['listen'] = '127.0.0.1:9174' + config['debug']['rlimit_core'] = 'unlimited' + config['debug']['slowlog'] = '/var/log/php/7.4/slow.log' + config['debug']['request_slowlog_timeout'] = '10s' + with open('/etc/php/7.4/fpm/pool.d/debug.conf', + encoding='utf-8', mode='w') as confifile: + Log.debug(self, "writting PHP 7.4 configuration into " + "/etc/php/7.4/fpm/pool.d/debug.conf") + config.write(confifile) + + with open("/etc/php/7.4/fpm/pool.d/debug.conf", + encoding='utf-8', mode='a') as myfile: + myfile.write( + "php_admin_value[xdebug.profiler_output_dir] " + "= /tmp/ \nphp_admin_value[xdebug.profiler_" + "output_name] = cachegrind.out.%p-%H-%R " + "\nphp_admin_flag[xdebug.profiler_enable" + "_trigger] = on \nphp_admin_flag[xdebug." + "profiler_enable] = off\n") + + # Disable xdebug + if not WOShellExec.cmd_exec( + self, "grep -q \';zend_extension\'" + " /etc/php/7.4/mods-available/xdebug.ini"): + WOFileUtils.searchreplace( + self, "/etc/php/7.4/mods-available/" + "xdebug.ini", + "zend_extension", ";zend_extension") + + # PHP and Debug pull configuration + if not os.path.exists('{0}22222/htdocs/fpm/status/' + .format(ngxroot)): + Log.debug(self, 'Creating directory ' + '{0}22222/htdocs/fpm/status/ ' + .format(ngxroot)) + os.makedirs('{0}22222/htdocs/fpm/status/' + .format(ngxroot)) + open('{0}22222/htdocs/fpm/status/debug74' + .format(ngxroot), + encoding='utf-8', mode='a').close() + open('{0}22222/htdocs/fpm/status/php74' + .format(ngxroot), + encoding='utf-8', mode='a').close() + + # Write info.php + if not os.path.exists('{0}22222/htdocs/php/' + .format(ngxroot)): + Log.debug(self, 'Creating directory ' + '{0}22222/htdocs/php/ ' + .format(ngxroot)) + os.makedirs('{0}22222/htdocs/php' + .format(ngxroot)) + + WOFileUtils.textwrite( + self, "{0}22222/htdocs/php/info.php" + .format(ngxroot), "") + + WOFileUtils.chown(self, "{0}22222/htdocs" + .format(ngxroot), + 'www-data', + 'www-data', recursive=True) + # check service restart or rollback configuration + if not WOService.restart_service(self, 'php7.4-fpm'): + WOGit.rollback(self, ["/etc/php"], msg="Rollback PHP") + else: + WOGit.add(self, ["/etc/php"], msg="Adding PHP into Git") + + if os.path.exists('/etc/nginx/conf.d/upstream.conf'): + if not WOFileUtils.grepcheck( + self, '/etc/nginx/conf.d/upstream.conf', 'php74'): + data = dict(php="9000", debug="9001", + php7="9070", debug7="9170", + release=WOVar.wo_version) + WOTemplate.deploy( + self, '/etc/nginx/conf.d/upstream.conf', + 'upstream.mustache', data, True) + WOConf.nginxcommon(self) + # create mysql config if it doesn't exist if "mariadb-server" in apt_packages: WOGit.add(self, ["/etc/mysql"], msg="Adding MySQL into Git") @@ -812,6 +926,23 @@ def post_pref(self, apt_packages, packages, upgrade=False): config_file.write(config) config_file.close() else: + if "PASSWORD" not in WOShellExec.cmd_exec_stdout( + self, 'mysql -e "use mysql; show grants;"'): + try: + if not os.path.exists('/etc/mysql/conf.d/my.cnf'): + Log.error(self, 'my.cnf not found') + config = configparser.ConfigParser() + config.read('/etc/mysql/conf.d/my.cnf') + chars = config['client']['password'] + WOShellExec.cmd_exec( + self, "mysql -e \"use mysql; " + "GRANT ALL PRIVILEGES on " + "*.* TO 'root'@'127.0.0.1' IDENTIFIED by " + "'{0}' WITH GRANT OPTION\"".format(chars)) + WOShellExec.cmd_exec( + self, 'mysql -e "flush privileges;"') + except CommandExecutionError: + Log.error(self, "Unable to set MySQL password") Log.info(self, "Tuning MariaDB configuration") if not os.path.isfile("/etc/mysql/my.cnf.default-pkg"): WOFileUtils.copyfile(self, "/etc/mysql/my.cnf", @@ -867,11 +998,12 @@ def post_pref(self, apt_packages, packages, upgrade=False): # create fail2ban configuration files if set(WOVar.wo_fail2ban).issubset(set(apt_packages)): + WOService.restart_service(self, 'fail2ban') WOGit.add(self, ["/etc/fail2ban"], msg="Adding Fail2ban into Git") if not os.path.isfile("/etc/fail2ban/jail.d/custom.conf"): Log.info(self, "Configuring Fail2Ban") - data = dict() + data = dict(release=WOVar.wo_version) WOTemplate.deploy( self, '/etc/fail2ban/jail.d/custom.conf', @@ -1463,7 +1595,7 @@ def pre_stack(self): if wo_check is False: # wo sysctl tweaks # check system type - wo_arch = bool(os.uname()[4] == 'x86_x64') + wo_arch = bool((os.uname()[4]) == 'x86_64') if os.path.isfile('/proc/1/environ'): # detect lxc containers wo_lxc = WOFileUtils.grepcheck( diff --git a/wo/cli/plugins/stack_services.py b/wo/cli/plugins/stack_services.py index 6b40649..01b3188 100644 --- a/wo/cli/plugins/stack_services.py +++ b/wo/cli/plugins/stack_services.py @@ -4,6 +4,7 @@ from cement.core.controller import CementBaseController, expose from wo.core.logging import Log from wo.core.services import WOService from wo.core.variables import WOVar +from wo.core.fileutils import WOFileUtils class WOStackStatusController(CementBaseController): @@ -20,17 +21,21 @@ class WOStackStatusController(CementBaseController): wo_system = "/lib/systemd/system/" pargs = self.app.pargs if not (pargs.nginx or pargs.php or + pargs.php72 or pargs.php73 or + pargs.php74 or pargs.mysql or pargs.redis or pargs.fail2ban or pargs.proftpd or - pargs.netdata): + pargs.netdata or + pargs.ufw): pargs.nginx = True pargs.php = True pargs.mysql = True pargs.fail2ban = True pargs.netdata = True + pargs.ufw = True if pargs.nginx: if os.path.exists('{0}'.format(wo_system) + 'nginx.service'): @@ -47,6 +52,16 @@ class WOStackStatusController(CementBaseController): services = services + ['php7.3-fpm'] else: Log.info(self, "PHP7.3-FPM is not installed") + if os.path.exists('{0}'.format(wo_system) + 'php7.4-fpm.service'): + services = services + ['php7.4-fpm'] + else: + Log.info(self, "PHP7.4-FPM is not installed") + + if pargs.php72: + if os.path.exists('{0}'.format(wo_system) + 'php7.2-fpm.service'): + services = services + ['php7.2-fpm'] + else: + Log.info(self, "PHP7.2-FPM is not installed") if pargs.php73: if os.path.exists('{0}'.format(wo_system) + 'php7.3-fpm.service'): @@ -54,6 +69,12 @@ class WOStackStatusController(CementBaseController): else: Log.info(self, "PHP7.3-FPM is not installed") + if pargs.php74: + if os.path.exists('{0}'.format(wo_system) + 'php7.4-fpm.service'): + services = services + ['php7.4-fpm'] + else: + Log.info(self, "PHP7.4-FPM is not installed") + if pargs.mysql: if ((WOVar.wo_mysql_host == "localhost") or (WOVar.wo_mysql_host == "127.0.0.1")): @@ -103,7 +124,8 @@ class WOStackStatusController(CementBaseController): wo_system = "/lib/systemd/system/" pargs = self.app.pargs if not (pargs.nginx or pargs.php or - pargs.php73 or + pargs.php72 or pargs.php73 or + pargs.php74 or pargs.mysql or pargs.fail2ban or pargs.netdata or @@ -128,6 +150,16 @@ class WOStackStatusController(CementBaseController): services = services + ['php7.3-fpm'] else: Log.info(self, "PHP7.3-FPM is not installed") + if os.path.exists('{0}'.format(wo_system) + 'php7.4-fpm.service'): + services = services + ['php7.4-fpm'] + else: + Log.info(self, "PHP7.4-FPM is not installed") + + if pargs.php72: + if os.path.exists('{0}'.format(wo_system) + 'php7.2-fpm.service'): + services = services + ['php7.2-fpm'] + else: + Log.info(self, "PHP7.2-FPM is not installed") if pargs.php73: if os.path.exists('{0}'.format(wo_system) + 'php7.3-fpm.service'): @@ -135,6 +167,12 @@ class WOStackStatusController(CementBaseController): else: Log.info(self, "PHP7.3-FPM is not installed") + if pargs.php74: + if os.path.exists('{0}'.format(wo_system) + 'php7.4-fpm.service'): + services = services + ['php7.4-fpm'] + else: + Log.info(self, "PHP7.4-FPM is not installed") + if pargs.mysql: if ((WOVar.wo_mysql_host == "localhost") or (WOVar.wo_mysql_host == "127.0.0.1")): @@ -184,7 +222,8 @@ class WOStackStatusController(CementBaseController): wo_system = "/lib/systemd/system/" pargs = self.app.pargs if not (pargs.nginx or pargs.php or - pargs.php73 or + pargs.php72 or pargs.php73 or + pargs.php74 or pargs.mysql or pargs.netdata or pargs.proftpd or @@ -210,6 +249,16 @@ class WOStackStatusController(CementBaseController): services = services + ['php7.3-fpm'] else: Log.info(self, "PHP7.3-FPM is not installed") + if os.path.exists('{0}'.format(wo_system) + 'php7.4-fpm.service'): + services = services + ['php7.4-fpm'] + else: + Log.info(self, "PHP7.4-FPM is not installed") + + if pargs.php72: + if os.path.exists('{0}'.format(wo_system) + 'php7.2-fpm.service'): + services = services + ['php7.2-fpm'] + else: + Log.info(self, "PHP7.2-FPM is not installed") if pargs.php73: if os.path.exists('{0}'.format(wo_system) + 'php7.3-fpm.service'): @@ -217,6 +266,12 @@ class WOStackStatusController(CementBaseController): else: Log.info(self, "PHP7.3-FPM is not installed") + if pargs.php74: + if os.path.exists('{0}'.format(wo_system) + 'php7.4-fpm.service'): + services = services + ['php7.4-fpm'] + else: + Log.info(self, "PHP7.4-FPM is not installed") + if pargs.mysql: if ((WOVar.wo_mysql_host == "localhost") or (WOVar.wo_mysql_host == "127.0.0.1")): @@ -266,7 +321,9 @@ class WOStackStatusController(CementBaseController): wo_system = "/lib/systemd/system/" pargs = self.app.pargs if not (pargs.nginx or pargs.php or + pargs.php72 or pargs.php73 or + pargs.php74 or pargs.mysql or pargs.netdata or pargs.proftpd or @@ -277,6 +334,7 @@ class WOStackStatusController(CementBaseController): pargs.mysql = True pargs.fail2ban = True pargs.netdata = True + pargs.ufw = True if pargs.nginx: if os.path.exists('{0}'.format(wo_system) + 'nginx.service'): @@ -293,6 +351,16 @@ class WOStackStatusController(CementBaseController): services = services + ['php7.3-fpm'] else: Log.info(self, "PHP7.3-FPM is not installed") + if os.path.exists('{0}'.format(wo_system) + 'php7.4-fpm.service'): + services = services + ['php7.4-fpm'] + else: + Log.info(self, "PHP7.4-FPM is not installed") + + if pargs.php72: + if os.path.exists('{0}'.format(wo_system) + 'php7.2-fpm.service'): + services = services + ['php7.2-fpm'] + else: + Log.info(self, "PHP7.2-FPM is not installed") if pargs.php73: if os.path.exists('{0}'.format(wo_system) + 'php7.3-fpm.service'): @@ -300,6 +368,12 @@ class WOStackStatusController(CementBaseController): else: Log.info(self, "PHP7.3-FPM is not installed") + if pargs.php74: + if os.path.exists('{0}'.format(wo_system) + 'php7.4-fpm.service'): + services = services + ['php7.4-fpm'] + else: + Log.info(self, "PHP7.4-FPM is not installed") + if pargs.mysql: if ((WOVar.wo_mysql_host == "localhost") or (WOVar.wo_mysql_host == "127.0.0.1")): @@ -338,6 +412,17 @@ class WOStackStatusController(CementBaseController): else: Log.info(self, "Netdata is not installed") + # UFW + if pargs.ufw: + if os.path.exists('/usr/sbin/ufw'): + if WOFileUtils.grepcheck( + self, '/etc/ufw/ufw.conf', 'ENABLED=yes'): + Log.info(self, "UFW Firewall is enabled") + else: + Log.info(self, "UFW Firewall is disabled") + else: + Log.info(self, "UFW is not installed") + for service in services: if WOService.get_service_status(self, service): Log.info(self, "{0:10}: {1}".format(service, "Running")) @@ -349,7 +434,8 @@ class WOStackStatusController(CementBaseController): wo_system = "/lib/systemd/system/" pargs = self.app.pargs if not (pargs.nginx or pargs.php or - pargs.php73 or + pargs.php72 or pargs.php73 or + pargs.php74 or pargs.mysql or pargs.netdata or pargs.proftpd or @@ -375,6 +461,16 @@ class WOStackStatusController(CementBaseController): services = services + ['php7.3-fpm'] else: Log.info(self, "PHP7.3-FPM is not installed") + if os.path.exists('{0}'.format(wo_system) + 'php7.4-fpm.service'): + services = services + ['php7.4-fpm'] + else: + Log.info(self, "PHP7.4-FPM is not installed") + + if pargs.php72: + if os.path.exists('{0}'.format(wo_system) + 'php7.2-fpm.service'): + services = services + ['php7.2-fpm'] + else: + Log.info(self, "PHP7.2-FPM is not installed") if pargs.php73: if os.path.exists('{0}'.format(wo_system) + 'php7.3-fpm.service'): @@ -382,6 +478,12 @@ class WOStackStatusController(CementBaseController): else: Log.info(self, "PHP7.3-FPM is not installed") + if pargs.php74: + if os.path.exists('{0}'.format(wo_system) + 'php7.4-fpm.service'): + services = services + ['php7.4-fpm'] + else: + Log.info(self, "PHP7.4-FPM is not installed") + if pargs.mysql: if ((WOVar.wo_mysql_host == "localhost") or (WOVar.wo_mysql_host == "127.0.0.1")): diff --git a/wo/cli/plugins/stack_upgrade.py b/wo/cli/plugins/stack_upgrade.py index 8894a8c..7df87fe 100644 --- a/wo/cli/plugins/stack_upgrade.py +++ b/wo/cli/plugins/stack_upgrade.py @@ -30,8 +30,12 @@ class WOStackUpgradeController(CementBaseController): dict(help='Upgrade Nginx stack', action='store_true')), (['--php'], dict(help='Upgrade PHP 7.2 stack', action='store_true')), + (['--php72'], + dict(help='Upgrade PHP 7.2 stack', action='store_true')), (['--php73'], dict(help='Upgrade PHP 7.3 stack', action='store_true')), + (['--php74'], + dict(help='Upgrade PHP 7.4 stack', action='store_true')), (['--mysql'], dict(help='Upgrade MySQL stack', action='store_true')), (['--wpcli'], @@ -44,8 +48,12 @@ class WOStackUpgradeController(CementBaseController): dict(help='Upgrade WordOps Dashboard', action='store_true')), (['--composer'], dict(help='Upgrade Composer', action='store_true')), + (['--mysqltuner'], + dict(help='Upgrade Composer', action='store_true')), (['--phpmyadmin'], dict(help='Upgrade phpMyAdmin', action='store_true')), + (['--adminer'], + dict(help='Upgrade Adminer', action='store_true')), (['--ngxblocker'], dict(help='Upgrade phpMyAdmin', action='store_true')), (['--no-prompt'], @@ -63,27 +71,33 @@ class WOStackUpgradeController(CementBaseController): packages = [] self.msg = [] pargs = self.app.pargs - if ((not pargs.web) and (not pargs.nginx) and - (not pargs.php) and (not pargs.php73) and + (not pargs.php) and + (not pargs.php72) and (not pargs.php73) and + (not pargs.php74) and (not pargs.mysql) and (not pargs.ngxblocker) and (not pargs.all) and (not pargs.wpcli) and (not pargs.netdata) and (not pargs.composer) and - (not pargs.phpmyadmin) and (not pargs.dashboard) and + (not pargs.phpmyadmin) and (not pargs.adminer) and + (not pargs.dashboard) and (not pargs.mysqltuner) and (not pargs.redis)): pargs.web = True pargs.admin = True + if pargs.php: + pargs.php72 = True + if pargs.all: pargs.web = True pargs.admin = True pargs.redis = True - pargs.php73 = True pargs.ngxblocker = True if pargs.web: pargs.nginx = True - pargs.php = True + pargs.php72 = True + pargs.php73 = True + pargs.php74 = True pargs.mysql = True pargs.wpcli = True @@ -93,6 +107,8 @@ class WOStackUpgradeController(CementBaseController): pargs.dashboard = True pargs.phpmyadmin = True pargs.wpcli = True + pargs.adminer = True + pargs.mysqltuner = True # nginx if pargs.nginx: @@ -106,34 +122,32 @@ class WOStackUpgradeController(CementBaseController): Log.info(self, "Nginx Stable is not already installed") # php 7.2 - if pargs.php: + if pargs.php72: if WOAptGet.is_installed(self, 'php7.2-fpm'): - apt_packages = apt_packages + WOVar.wo_php + \ + apt_packages = apt_packages + WOVar.wo_php72 + \ WOVar.wo_php_extra - else: - Log.info(self, "PHP 7.2 is not installed") # php 7.3 if pargs.php73: if WOAptGet.is_installed(self, 'php7.3-fpm'): apt_packages = apt_packages + WOVar.wo_php73 + \ WOVar.wo_php_extra - else: - Log.info(self, "PHP 7.3 is not installed") + + # php 7.4 + if pargs.php74: + if WOAptGet.is_installed(self, 'php7.4-fpm'): + apt_packages = apt_packages + WOVar.wo_php74 + \ + WOVar.wo_php_extra # mysql if pargs.mysql: if WOShellExec.cmd_exec(self, 'mysqladmin ping'): apt_packages = apt_packages + ['mariadb-server'] - else: - Log.info(self, "MariaDB is not installed") # redis if pargs.redis: if WOAptGet.is_installed(self, 'redis-server'): apt_packages = apt_packages + ['redis-server'] - else: - Log.info(self, "Redis is not installed") # wp-cli if pargs.wpcli: @@ -188,6 +202,32 @@ class WOStackUpgradeController(CementBaseController): else: Log.info(self, "phpMyAdmin isn't installed") + # adminer + if pargs.adminer: + if os.path.isfile("{0}22222/htdocs/db/" + "adminer/index.php" + .format(WOVar.wo_webroot)): + Log.debug(self, "Setting packages variable for Adminer ") + packages = packages + [[ + "https://github.com/vrana/adminer/" + "releases/download/v{0}" + "/adminer-{0}.php" + .format(WOVar.wo_adminer), + "{0}22222/" + "htdocs/db/adminer/index.php" + .format(WOVar.wo_webroot), + "Adminer"], + ["https://raw.githubusercontent.com" + "/vrana/adminer/master/designs/" + "pepa-linha/adminer.css", + "{0}22222/" + "htdocs/db/adminer/adminer.css" + .format(WOVar.wo_webroot), + "Adminer theme"]] + else: + Log.debug(self, "Adminer isn't installed") + Log.info(self, "Adminer isn't installed") + # composer if pargs.composer: if os.path.isfile('/usr/local/bin/composer'): @@ -198,6 +238,18 @@ class WOStackUpgradeController(CementBaseController): else: Log.info(self, "Composer isn't installed") + # mysqltuner + if pargs.mysqltuner: + if WOAptGet.is_exec(self, 'mysqltuner'): + Log.debug(self, "Setting packages variable " + "for MySQLTuner ") + packages = packages + [["https://raw." + "githubusercontent.com/" + "major/MySQLTuner-perl" + "/master/mysqltuner.pl", + "/usr/bin/mysqltuner", + "MySQLTuner"]] + # ngxblocker if pargs.ngxblocker: if os.path.exists('/usr/local/sbin/install-ngxblocker'): @@ -217,6 +269,8 @@ class WOStackUpgradeController(CementBaseController): if (apt_packages): if (("php7.2-fpm" not in apt_packages) and ("php7.3-fpm" not in apt_packages) and + ("php7.4-fpm" not in apt_packages) and + ("redis-server" not in apt_packages) and ("nginx-custom" not in apt_packages) and ("mariadb-server" not in apt_packages)): pass @@ -243,6 +297,9 @@ class WOStackUpgradeController(CementBaseController): if "php7.3-fpm" in apt_packages: WOAptGet.remove(self, ['php7.3-fpm'], auto=False, purge=True) + if "php7.4-fpm" in apt_packages: + WOAptGet.remove(self, ['php7.4-fpm'], + auto=False, purge=True) # check if nginx upgrade is blocked if os.path.isfile( '/etc/apt/preferences.d/nginx-block'): @@ -257,16 +314,16 @@ class WOStackUpgradeController(CementBaseController): # Post Actions after package updates if (packages): - if pargs.wpcli: + if WOAptGet.is_selected(self, 'WP-CLI', packages): WOFileUtils.rm(self, '/usr/local/bin/wp') - if pargs.netdata: + if WOAptGet.is_selected(self, 'Netdata', packages): WOFileUtils.rm(self, '/var/lib/wo/tmp/kickstart.sh') - if pargs.ngxblocker: + if WOAptGet.is_selected(self, 'ngxblocker', packages): WOFileUtils.rm(self, '/usr/local/sbin/update-ngxblocker') - if pargs.dashboard: + if WOAptGet.is_selected(self, 'WordOps Dashboard', packages): if os.path.isfile('/var/www/22222/htdocs/index.php'): WOFileUtils.rm(self, '/var/www/22222/htdocs/index.php') if os.path.isfile('/var/www/22222/htdocs/index.html'): @@ -276,18 +333,22 @@ class WOStackUpgradeController(CementBaseController): Log.debug(self, "Downloading following: {0}".format(packages)) WODownload.download(self, packages) - if pargs.wpcli: + if WOAptGet.is_selected(self, 'WP-CLI', packages): WOFileUtils.chmod(self, "/usr/local/bin/wp", 0o775) - if pargs.ngxblocker: + if WOAptGet.is_selected(self, 'ngxblocker', packages): WOFileUtils.chmod( - self, '/usr/local/sbin/update-ngxblocker', 0o700) + self, '/usr/local/sbin/update-ngxblocker', 0o775) WOShellExec.cmd_exec( - self, '/usr/local/sbin/update-ngxblocker -nq' - ) + self, '/usr/local/sbin/update-ngxblocker -nq') + + if WOAptGet.is_selected(self, 'MySQLTuner', packages): + WOFileUtils.chmod(self, "/usr/bin/mysqltuner", 0o775) + if os.path.exists('/usr/local/bin/mysqltuner'): + WOFileUtils.rm(self, '/usr/local/bin/mysqltuner') # Netdata - if pargs.netdata: + if WOAptGet.is_selected(self, 'Netdata', packages): Log.wait(self, "Upgrading Netdata") # detect static binaries install if os.path.isdir('/opt/netdata'): @@ -313,7 +374,7 @@ class WOStackUpgradeController(CementBaseController): self, "bash /var/lib/wo/tmp/kickstart.sh") Log.valide(self, "Upgrading Netdata") - if pargs.dashboard: + if WOAptGet.is_selected(self, 'WordOps Dashboard', packages): post_pref( self, [], [["https://github.com/WordOps" "/wordops-dashboard/" @@ -323,7 +384,7 @@ class WOStackUpgradeController(CementBaseController): "/var/lib/wo/tmp/wo-dashboard.tar.gz", "WordOps Dashboard"]]) - if pargs.composer: + if WOAptGet.is_selected(self, 'Composer', packages): Log.wait(self, "Upgrading Composer") if WOShellExec.cmd_exec( self, '/usr/bin/php -v'): @@ -336,7 +397,7 @@ class WOStackUpgradeController(CementBaseController): WOFileUtils.chmod(self, "/usr/local/bin/composer", 0o775) Log.valide(self, "Upgrading Composer ") - if pargs.phpmyadmin: + if WOAptGet.is_selected(self, 'PHPMyAdmin', packages): Log.wait(self, "Upgrading phpMyAdmin") WOExtract.extract(self, '/var/lib/wo/tmp/pma.tar.gz', '/var/lib/wo/tmp/') @@ -359,5 +420,10 @@ class WOStackUpgradeController(CementBaseController): 'www-data', 'www-data', recursive=True) Log.valide(self, "Upgrading phpMyAdmin") + if os.path.exists('{0}22222/htdocs'.format(WOVar.wo_webroot)): + WOFileUtils.chown(self, "{0}22222/htdocs" + .format(WOVar.wo_webroot), + 'www-data', + 'www-data', recursive=True) Log.info(self, "Successfully updated packages") diff --git a/wo/cli/plugins/sync.py b/wo/cli/plugins/sync.py index 872f962..0dc3e16 100644 --- a/wo/cli/plugins/sync.py +++ b/wo/cli/plugins/sync.py @@ -1,4 +1,5 @@ import glob +import os from cement.core.controller import CementBaseController, expose @@ -39,6 +40,13 @@ class WOSyncController(CementBaseController): # Read config files configfiles = glob.glob(wo_site_webroot + '/*-config.php') + if (os.path.exists( + '{0}/ee-config.php'.format(wo_site_webroot)) and + os.path.exists( + '{0}/wo-config.php'.format(wo_site_webroot))): + configfiles = glob.glob( + wo_site_webroot + 'wo-config.php') + # search for wp-config.php inside htdocs/ if not configfiles: Log.debug(self, "Config files not found in {0}/ " diff --git a/wo/cli/templates/22222.mustache b/wo/cli/templates/22222.mustache index e34fa36..ef456cb 100644 --- a/wo/cli/templates/22222.mustache +++ b/wo/cli/templates/22222.mustache @@ -1,4 +1,4 @@ -# WordOps admin NGINX CONFIGURATION - WO v3.9.7 +# WordOps admin NGINX CONFIGURATION - WordOps {{release}} server { diff --git a/wo/cli/templates/fail2ban.mustache b/wo/cli/templates/fail2ban.mustache index 2cf4d83..6d918cd 100644 --- a/wo/cli/templates/fail2ban.mustache +++ b/wo/cli/templates/fail2ban.mustache @@ -6,19 +6,21 @@ enabled = true [nginx-http-auth] enabled = true +logpath = /var/log/nginx/*error*.log [nginx-botsearch] enabled = true +logpath = /var/log/nginx/*access*.log [wo-wordpress] enabled = true filter = wo-wordpress action = iptables-multiport[name="wo-wordpress", port="http,https"] -logpath = /var/log/nginx/*access.log +logpath = /var/log/nginx/*access*.log maxretry = 5 [nginx-forbidden] enabled = true filter = nginx-forbidden -action = iptables-multiport[name="wo-wordpress", port="http,https"] +action = iptables-multiport[name="nginx-forbidden", port="http,https"] logpath = /var/log/nginx/*error*.log diff --git a/wo/cli/templates/gzip.mustache b/wo/cli/templates/gzip.mustache index ff591fa..04d9b1a 100644 --- a/wo/cli/templates/gzip.mustache +++ b/wo/cli/templates/gzip.mustache @@ -6,6 +6,7 @@ gzip_disable "msie6"; gzip_vary on; + gzip_static on; gzip_proxied any; gzip_comp_level 6; gzip_buffers 16 8k; diff --git a/wo/cli/templates/locations.mustache b/wo/cli/templates/locations.mustache index d0f1c17..dace54f 100644 --- a/wo/cli/templates/locations.mustache +++ b/wo/cli/templates/locations.mustache @@ -1,4 +1,4 @@ -# NGINX CONFIGURATION FOR COMMON LOCATION - WO v3.9.7 +# NGINX CONFIGURATION FOR COMMON LOCATION - WordOps {{release}} # DO NOT MODIFY, ALL CHANGES WILL BE LOST AFTER AN WordOps (wo) UPDATE # Basic locations files location = /favicon.ico { @@ -41,7 +41,7 @@ location ~* "/(^$|readme|license|example|README|LEGALNOTICE|INSTALLATION|CHANGE deny all; } # Deny backup extensions & log files and return 403 forbidden -location ~* "\.(old|orig|original|php#|php~|php_bak|save|swo|aspx?|tpl|sh|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out|sql|svn|swp|tar|rdf)$" { +location ~* "\.(old|orig|original|php#|php~|php_bak|save|swo|aspx?|tpl|sh|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out|sql|svn|swp|tar|rdf|gz|zip|bz2|7z|pem|asc|conf|dump)$" { deny all; } location ~* "/(=|\$&|_mm|(wp-)?config\.|cgi-|etc/passwd|muieblack)" { diff --git a/wo/cli/templates/map-wp.mustache b/wo/cli/templates/map-wp.mustache index cd5f4b9..0133b75 100644 --- a/wo/cli/templates/map-wp.mustache +++ b/wo/cli/templates/map-wp.mustache @@ -1,4 +1,4 @@ -# NGINX CONFIGURATION FOR FASTCGI_CACHE EXCEPTION - WO v3.9.8 +# NGINX CONFIGURATION FOR FASTCGI_CACHE EXCEPTION - WordOps {{release}} # DO NOT MODIFY, ALL CHANGES WILL BE LOST AFTER AN WordOps (wo) UPDATE # do not cache xhtml request diff --git a/wo/cli/templates/nginx-core.mustache b/wo/cli/templates/nginx-core.mustache index 97fc476..4ce92dc 100644 --- a/wo/cli/templates/nginx-core.mustache +++ b/wo/cli/templates/nginx-core.mustache @@ -16,7 +16,7 @@ events { http { ## - # WordOps Settings + # WordOps Settings - WordOps {{release}} ## keepalive_timeout 8; @@ -51,17 +51,18 @@ http { # SSL Settings ## + # Enable 0-RTT support for TLS 1.3 + proxy_set_header Early-Data $ssl_early_data; + ssl_early_data on; + ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; ssl_prefer_server_ciphers on; - ssl_early_data on; - {{#tls13}}ssl_ciphers 'TLS13+AESGCM+AES256:TLS13+AESGCM+AES128:TLS13+CHACHA20:EECDH+AESGCM:EECDH+CHACHA20'; - ssl_protocols TLSv1.2 TLSv1.3;{{/tls13}} + ssl_ciphers 'TLS13+AESGCM+AES256:TLS13+AESGCM+AES128:TLS13+CHACHA20:EECDH+AESGCM:EECDH+CHACHA20'; + ssl_protocols TLSv1.2 TLSv1.3; ssl_ecdh_curve X25519:P-521:P-384:P-256; - {{^tls13}}# Previous TLS v1.2 configuration - ssl_protocols TLSv1.2; - ssl_ciphers EECDH+CHACHA20:EECDH+AESGCM:EECDH+AES;{{/tls13}} + # Common security headers more_set_headers "X-Frame-Options : SAMEORIGIN"; diff --git a/wo/cli/templates/php.mustache b/wo/cli/templates/php.mustache index 27c861d..3387663 100644 --- a/wo/cli/templates/php.mustache +++ b/wo/cli/templates/php.mustache @@ -1,4 +1,4 @@ -# PHP NGINX CONFIGURATION - WO v3.9.7 +# PHP NGINX CONFIGURATION - WordOps {{release}} # DO NOT MODIFY, ALL CHANGES WILL BE LOST AFTER AN WordOps (wo) UPDATE location / { try_files $uri $uri/ /index.php$is_args$args; diff --git a/wo/cli/templates/publicsuffix.mustache b/wo/cli/templates/publicsuffix.mustache new file mode 100644 index 0000000..c1b52e7 --- /dev/null +++ b/wo/cli/templates/publicsuffix.mustache @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# WordOps script to download public suffix list from Github + +# check if curl is available +if ! { command -v curl; }; then + apt-get update && apt-get install curl -qq > /dev/null 2>&1 +fi +# download the list +rm -f /var/lib/wo/public_suffix_list.dat +curl -sL -m 30 --retry 3 -k https://raw.githubusercontent.com/publicsuffix/list/master/public_suffix_list.dat | sed '/^\/\//d' | sed '/^$/d' | sed 's/^\s+//g' > /var/lib/wo/public_suffix_list.dat diff --git a/wo/cli/templates/redis.mustache b/wo/cli/templates/redis.mustache index 7864dd9..0f6965d 100644 --- a/wo/cli/templates/redis.mustache +++ b/wo/cli/templates/redis.mustache @@ -1,4 +1,4 @@ -# Redis NGINX CONFIGURATION - WO v3.9.7 +# Redis NGINX CONFIGURATION - WordOps {{release}} # DO NOT MODIFY, ALL CHANGES WILL BE LOST AFTER AN WordOps (wo) UPDATE # $skip_cache variable set in /etc/nginx/conf.d/map-wp.conf diff --git a/wo/cli/templates/siteinfo.mustache b/wo/cli/templates/siteinfo.mustache index e196175..d87d86d 100644 --- a/wo/cli/templates/siteinfo.mustache +++ b/wo/cli/templates/siteinfo.mustache @@ -2,13 +2,17 @@ Information about {{domain}}: Nginx configuration {{type}} {{enable}} {{#php_version}}PHP Version {{php_version}}{{/php_version}} -{{#ssl}}SSL {{ssl}}{{/ssl}} -{{#sslprovider}}SSL PROVIDER {{sslprovider}}{{/sslprovider}} -{{#sslexpiry}}SSL EXPIRY DATE {{sslexpiry}}{{/sslexpiry}} + +{{#ssl}}SSL {{ssl}}{{/ssl}}{{#sslprovider}} +SSL PROVIDER {{sslprovider}}{{/sslprovider}}{{#sslexpiry}} +SSL EXPIRY DATE {{sslexpiry}}{{/sslexpiry}} + access_log {{accesslog}} error_log {{errorlog}} {{#webroot}}Webroot {{webroot}}{{/webroot}} -{{#dbname}}DB_NAME {{dbname}}{{/dbname}} + +{{#dbname}} +DB_NAME {{dbname}}{{/dbname}} {{#dbname}}DB_USER {{dbuser}}{{/dbname}} {{#dbname}}DB_PASS {{dbpass}}{{/dbname}} {{#tablepref}}table_prefix {{tableprefix}}{{/tablepref}} diff --git a/wo/cli/templates/tweaks.mustache b/wo/cli/templates/tweaks.mustache index ea2ee2b..2912372 100644 --- a/wo/cli/templates/tweaks.mustache +++ b/wo/cli/templates/tweaks.mustache @@ -1,4 +1,4 @@ -# NGINX Tweaks - WO v3.9.8 +# NGINX Tweaks - WordOps {{release}} directio 4m; directio_alignment 512; http2_max_field_size 16k; diff --git a/wo/cli/templates/upstream.mustache b/wo/cli/templates/upstream.mustache index 27d290a..8d6e25f 100644 --- a/wo/cli/templates/upstream.mustache +++ b/wo/cli/templates/upstream.mustache @@ -1,4 +1,4 @@ -# NGINX UPSTREAM CONFIGURATION - WO v3.9.8 +# NGINX UPSTREAM CONFIGURATION - WordOps {{release}} # DO NOT MODIFY, ALL CHANGES WILL BE LOST AFTER AN WordOps (wo) UPDATE #------------------------------- # PHP 5.6 @@ -41,8 +41,8 @@ upstream php72 { # PHP 7.2 debug upstream debug72 { -# Debug Pool -server 127.0.0.1:9172; + # Debug Pool + server 127.0.0.1:9172; } #------------------------------- @@ -61,10 +61,30 @@ upstream php73 { # PHP 7.3 debug upstream debug73 { -# Debug Pool + # Debug Pool server 127.0.0.1:9173; } +#------------------------------- +# PHP 7.4 +#------------------------------- + +# PHP 7.4 upstream with load-balancing on two unix sockets +upstream php74 { + least_conn; + + server unix:/var/run/php/php74-fpm.sock; + server unix:/var/run/php/php74-two-fpm.sock; + + keepalive 5; +} + +# PHP 7.4 debug +upstream debug74 { + # Debug Pool + server 127.0.0.1:9174; +} + #------------------------------- # Netdata #------------------------------- diff --git a/wo/cli/templates/virtualconf-php7.mustache b/wo/cli/templates/virtualconf-php7.mustache deleted file mode 100644 index 09a99bd..0000000 --- a/wo/cli/templates/virtualconf-php7.mustache +++ /dev/null @@ -1,58 +0,0 @@ - -server { - - {{#multisite}} - # Uncomment the following line for domain mapping - # listen 80 default_server; - {{/multisite}} - - server_name {{site_name}} {{#multisite}}*{{/multisite}}{{^multisite}}www{{/multisite}}.{{site_name}}; - - {{#multisite}} - # Uncomment the following line for domain mapping - #server_name_in_redirect off; - {{/multisite}} - - access_log /var/log/nginx/{{site_name}}.access.log {{^wpredis}}{{^static}}rt_cache{{/static}}{{/wpredis}}{{#wpredis}}rt_cache_redis{{/wpredis}}; - error_log /var/log/nginx/{{site_name}}.error.log; - - {{#proxy}} - add_header X-Proxy-Cache $upstream_cache_status; - location / { - proxy_pass http://{{host}}:{{port}}; - proxy_redirect off; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - - # Security settings for better privacy - # Deny hidden files - location ~ /\.(?!well-known\/) { - deny all; - } - # letsencrypt validation - location /.well-known/acme-challenge/ { - alias /var/www/html/.well-known/acme-challenge/; - allow all; - } - {{/proxy}} - - {{^proxy}} - root {{webroot}}/htdocs; - - index {{^static}}index.php{{/static}} index.html index.htm; - - {{#static}} - location / { - try_files $uri $uri/ =404; - } - {{/static}} - - {{^static}}include {{#basic}}common/php73.conf;{{/basic}}{{#wpfc}}common/wpfc-php73.conf;{{/wpfc}} {{#wpsc}}common/wpsc-php73.conf;{{/wpsc}}{{#wpredis}}common/redis-php73.conf;{{/wpredis}}{{#wprocket}}common/wprocket-php73.conf;{{/wprocket}}{{#wpce}}common/wpce-php73.conf;{{/wpce}} - {{#wpsubdir}}include common/wpsubdir.conf;{{/wpsubdir}}{{/static}} - {{#wp}}include common/wpcommon-php73.conf;{{/wp}} - include common/locations-wo.conf;{{/proxy}} - include {{webroot}}/conf/nginx/*.conf; - -} diff --git a/wo/cli/templates/virtualconf.mustache b/wo/cli/templates/virtualconf.mustache index 098d6cc..76be634 100644 --- a/wo/cli/templates/virtualconf.mustache +++ b/wo/cli/templates/virtualconf.mustache @@ -49,9 +49,9 @@ server { } {{/static}} - {{^static}}include {{#basic}}common/php72.conf;{{/basic}}{{#wpfc}}common/wpfc-php72.conf;{{/wpfc}}{{#wpsc}}common/wpsc-php72.conf;{{/wpsc}}{{#wpredis}}common/redis-php72.conf;{{/wpredis}}{{#wprocket}}common/wprocket-php72.conf;{{/wprocket}}{{#wpce}}common/wpce-php72.conf;{{/wpce}} + {{^static}}include {{#basic}}common/{{wo_php}}.conf;{{/basic}}{{#wpfc}}common/wpfc-{{wo_php}}.conf;{{/wpfc}}{{#wpsc}}common/wpsc-{{wo_php}}.conf;{{/wpsc}}{{#wpredis}}common/redis-{{wo_php}}.conf;{{/wpredis}}{{#wprocket}}common/wprocket-{{wo_php}}.conf;{{/wprocket}}{{#wpce}}common/wpce-{{wo_php}}.conf;{{/wpce}} {{#wpsubdir}}include common/wpsubdir.conf;{{/wpsubdir}}{{/static}} - {{#wp}}include common/wpcommon-php72.conf;{{/wp}} + {{#wp}}include common/wpcommon-{{wo_php}}.conf;{{/wp}} include common/locations-wo.conf;{{/proxy}} include {{webroot}}/conf/nginx/*.conf; diff --git a/wo/cli/templates/webp.mustache b/wo/cli/templates/webp.mustache index d53dc46..aa84b2e 100644 --- a/wo/cli/templates/webp.mustache +++ b/wo/cli/templates/webp.mustache @@ -1,7 +1,39 @@ -# WEBP NGINX CONFIGURATION - WO v3.9.7 +# WEBP NGINX CONFIGURATION - WordOps {{release}} # DO NOT MODIFY, ALL CHANGES WILL BE LOST AFTER AN WordOps (wo) UPDATE -map $http_accept $webp_suffix { - default ""; - "~*webp" ".webp"; +map $http_accept $webp_suffix_valid { + default 1; + "~*webp" 0; +} + +map $realip_remote_addr $webp_suffix_cf { + default 0; + 103.21.244.0/22 1; + 103.22.200.0/22 1; + 103.31.4.0/22 1; + 104.16.0.0/12 1; + 108.162.192.0/18 1; + 131.0.72.0/22 1; + 141.101.64.0/18 1; + 162.158.0.0/15 1; + 172.64.0.0/13 1; + 173.245.48.0/20 1; + 188.114.96.0/20 1; + 190.93.240.0/20 1; + 197.234.240.0/22 1; + 198.41.128.0/17 1; + 199.27.128.0/21 1; + 2400:cb00::/32 1; + 2405:8100::/32 1; + 2405:b500::/32 1; + 2606:4700::/32 1; + 2803:f800::/32 1; + 2a06:98c0::/29 1; + 2c0f:f248::/32 1; + +} + +map $webp_suffix_cf$webp_suffix_valid $webp_suffix { + default ""; + 00 ".webp"; } \ No newline at end of file diff --git a/wo/cli/templates/wo-update.mustache b/wo/cli/templates/wo-update.mustache index f1ab266..a157bb5 100644 --- a/wo/cli/templates/wo-update.mustache +++ b/wo/cli/templates/wo-update.mustache @@ -26,15 +26,16 @@ fi if [ -n "$CURRENT_RELEASE" ] && [ -n "$LATEST_RELEASE" ]; then if [ "$CURRENT_RELEASE" != "$LATEST_RELEASE" ]; then # display message with motd-news on Ubuntu - echo '*** A new WordOps release is available ***' > "$NEWS" 2> "$ERR" + echo '*** A new WordOps release is available ***' >"$NEWS" 2>"$ERR" echo # At most, 10 lines of text, remove control characters, print at most 80 characters per line safe_print "$NEWS" # Try to update the cache - safe_print "$NEWS" 2> /dev/null > $CACHE || true + safe_print "$NEWS" 2>/dev/null >$CACHE || true else # clean news - echo '' > "$NEWS" 2> "$ERR" - safe_print "$NEWS" 2> /dev/null > $CACHE || true + echo '' >"$NEWS" 2>"$ERR" + safe_print "$NEWS" 2>/dev/null >$CACHE || true + fi fi diff --git a/wo/cli/templates/wpce.mustache b/wo/cli/templates/wpce.mustache index f907397..597e735 100644 --- a/wo/cli/templates/wpce.mustache +++ b/wo/cli/templates/wpce.mustache @@ -1,4 +1,4 @@ -# WPCE NGINX CONFIGURATION - WO v3.9.8 +# WPCE NGINX CONFIGURATION - WordOps {{release}} # DO NOT MODIFY, ALL CHANGES WILL BE LOST AFTER AN WordOps (wo) UPDATE # $cache_uri variable set in /etc/nginx/conf.d/map-wp.conf # Use cached or actual file if they exists, Otherwise pass request to WordPress @@ -10,7 +10,7 @@ location ~ \.php$ { include fastcgi_params; fastcgi_pass {{upstream}}; } -location ~ /wp-content/cache/cache-enabler/.*html$ { +location ~ /wp-content/cache/cache-enabler/*\.html$ { etag on; add_header Vary "Accept-Encoding, Cookie"; access_log off; diff --git a/wo/cli/templates/wpcommon.mustache b/wo/cli/templates/wpcommon.mustache index fdf2ad2..8b1b9fe 100644 --- a/wo/cli/templates/wpcommon.mustache +++ b/wo/cli/templates/wpcommon.mustache @@ -1,4 +1,4 @@ -# WordPress COMMON SETTINGS - WO v3.9.7 +# WordPress COMMON SETTINGS - WordOps {{release}} # DO NOT MODIFY, ALL CHANGES WILL BE LOST AFTER AN WordOps (wo) UPDATE # Limit access to avoid brute force attack location = /wp-login.php { @@ -12,7 +12,7 @@ location = /wp-cron.php { include fastcgi_params; fastcgi_pass {{upstream}}; } -# Prevent Dos attacks with xmlrpc.php +# Prevent DoS attacks with xmlrpc.php location = /xmlrpc.php { limit_req zone=two burst=1 nodelay; include fastcgi_params; @@ -41,7 +41,7 @@ location /wp-content/uploads { location ~ \.(png|jpe?g)$ { add_header Vary "Accept-Encoding"; more_set_headers 'Access-Control-Allow-Origin : *'; - add_header Cache-Control "public, no-transform"; + more_set_headers "Cache-Control : public, no-transform"; access_log off; log_not_found off; expires max; @@ -57,7 +57,7 @@ location /wp-content/plugins/ewww-image-optimizer/images { location ~ \.(png|jpe?g)$ { add_header Vary "Accept-Encoding"; more_set_headers 'Access-Control-Allow-Origin : *'; - add_header Cache-Control "public, no-transform"; + more_set_headers "Cache-Control : public, no-transform"; access_log off; log_not_found off; expires max; diff --git a/wo/cli/templates/wpfc.mustache b/wo/cli/templates/wpfc.mustache index 5dbae7c..30467ed 100644 --- a/wo/cli/templates/wpfc.mustache +++ b/wo/cli/templates/wpfc.mustache @@ -1,4 +1,4 @@ -# WPFC NGINX CONFIGURATION - WO v3.9.7 +# WPFC NGINX CONFIGURATION - WordOps {{release}} # DO NOT MODIFY, ALL CHANGES WILL BE LOST AFTER AN WordOps (wo) UPDATE # $skip_cache variable set in /etc/nginx/conf.d/map-wp.conf diff --git a/wo/cli/templates/wprocket.mustache b/wo/cli/templates/wprocket.mustache index d2be90d..985fde3 100644 --- a/wo/cli/templates/wprocket.mustache +++ b/wo/cli/templates/wprocket.mustache @@ -1,4 +1,4 @@ -# WPROCKET NGINX CONFIGURATION - WO v3.9.8 +# WPROCKET NGINX CONFIGURATION - WordOps {{release}} # DO NOT MODIFY, ALL CHANGES WILL BE LOST AFTER AN WordOps (wo) UPDATE # $cache_uri variable set in /etc/nginx/conf.d/map-wp.conf # Use cached or actual file if they exists, Otherwise pass request to WordPress @@ -10,8 +10,9 @@ location ~ \.php$ { include fastcgi_params; fastcgi_pass {{upstream}}; } -location ~ /wp-content/cache/wp-rocket/.*html$ { +location ~ /wp-content/cache/wp-rocket/*\.html$ { etag on; + gzip_static on; add_header Vary "Accept-Encoding, Cookie"; access_log off; log_not_found off; diff --git a/wo/cli/templates/wpsc.mustache b/wo/cli/templates/wpsc.mustache index ce5baa7..1394692 100644 --- a/wo/cli/templates/wpsc.mustache +++ b/wo/cli/templates/wpsc.mustache @@ -1,4 +1,4 @@ -# WPSC NGINX CONFIGURATION - WO v3.9.7 +# WPSC NGINX CONFIGURATION - WordOps {{release}} # DO NOT MODIFY, ALL CHANGES WILL BE LOST AFTER AN WordOps (wo) UPDATE # $cache_uri variable set in /etc/nginx/conf.d/map-wp.conf diff --git a/wo/core/acme.py b/wo/core/acme.py index 4c6434d..330f778 100644 --- a/wo/core/acme.py +++ b/wo/core/acme.py @@ -16,8 +16,41 @@ class WOAcme: wo_acme_exec = ("/etc/letsencrypt/acme.sh --config-home " "'/etc/letsencrypt/config'") + def check_acme(self): + """ + Check if acme.sh is properly installed, + and install it if required + """ + if not os.path.exists('/etc/letsencrypt/acme.sh'): + if os.path.exists('/opt/acme.sh'): + WOFileUtils.rm(self, '/opt/acme.sh') + WOGit.clone( + self, 'https://github.com/Neilpang/acme.sh.git', + '/opt/acme.sh', branch='master') + WOFileUtils.mkdir(self, '/etc/letsencrypt/config') + WOFileUtils.mkdir(self, '/etc/letsencrypt/renewal') + WOFileUtils.mkdir(self, '/etc/letsencrypt/live') + try: + WOFileUtils.chdir(self, '/opt/acme.sh') + WOShellExec.cmd_exec( + self, './acme.sh --install --home /etc/letsencrypt' + '--config-home /etc/letsencrypt/config' + '--cert-home /etc/letsencrypt/renewal' + ) + WOShellExec.cmd_exec( + self, "{0} --upgrade --auto-upgrade" + .format(WOAcme.wo_acme_exec) + ) + except CommandExecutionError as e: + Log.debug(self, str(e)) + Log.error(self, "acme.sh installation failed") + if not os.path.exists('/etc/letsencrypt/acme.sh'): + Log.error(self, 'acme.sh ') + def export_cert(self): """Export acme.sh csv certificate list""" + # check acme.sh is installed + WOAcme.check_acme(self) if not WOShellExec.cmd_exec( self, "{0} ".format(WOAcme.wo_acme_exec) + "--list --listraw > /var/lib/wo/cert.csv"): @@ -26,6 +59,9 @@ class WOAcme: def setupletsencrypt(self, acme_domains, acmedata): """Issue SSL certificates with acme.sh""" + # check acme.sh is installed + WOAcme.check_acme(self) + # define variables all_domains = '\' -d \''.join(acme_domains) wo_acme_dns = acmedata['acme_dns'] keylenght = acmedata['keylength'] @@ -74,6 +110,8 @@ class WOAcme: def deploycert(self, wo_domain_name): """Deploy Let's Encrypt certificates with acme.sh""" + # check acme.sh is installed + WOAcme.check_acme(self) if not os.path.isfile('/etc/letsencrypt/renewal/{0}_ecc/fullchain.cer' .format(wo_domain_name)): Log.error(self, 'Certificate not found. Deployment canceled') @@ -135,6 +173,8 @@ class WOAcme: def renew(self, domain): """Renew letsencrypt certificate with acme.sh""" + # check acme.sh is installed + WOAcme.check_acme(self) try: WOShellExec.cmd_exec( self, "{0} ".format(WOAcme.wo_acme_exec) + @@ -158,7 +198,10 @@ class WOAcme: response = requests.get(url, headers=headers).json() domain_ip = response["Answer"][0]['data'] except requests.RequestException: - Log.error(self, 'Resolving domain IP failed') + Log.error( + self, 'Resolving domain IP failed.\n' + 'The domain {0} do not exist or a DNS record is missing' + .format(domain)) if(not domain_ip == server_ip): Log.warn( self, "{0}".format(domain) + @@ -202,6 +245,8 @@ class WOAcme: .format(WOVar.wo_ssl_live, domain), '/etc/letsencrypt/shared/{0}.conf'.format(domain)] wo_domain = domain + # check acme.sh is installed + WOAcme.check_acme(self) if WOAcme.cert_check(self, wo_domain): Log.info(self, "Removing Acme configuration") Log.debug(self, "Removing Acme configuration") diff --git a/wo/core/aptget.py b/wo/core/aptget.py index 2fe2be9..6a7ce3e 100644 --- a/wo/core/aptget.py +++ b/wo/core/aptget.py @@ -1,6 +1,7 @@ """WordOps package installation using apt-get module.""" import subprocess import sys +import os from sh import ErrorReturnCode, apt_get @@ -222,6 +223,29 @@ class WOAptGet(): # apt_cache.close() return False + def is_exec(self, package_name): + """ + Check if package is available by looking + for an executable or a systemd service related + to this package + """ + exec_path = ["/bin", "/usr/bin", "/usr/local/bin", + "/usr/sbin", "/usr/local/sbin"] + for path in exec_path: + if os.path.exists('{0}/{1}'.format(path, package_name)): + return True + return False + + def is_selected(self, package_name, packages_list): + """ + Check if package is selected for install/removal/purge + in packages_list + """ + for package in packages_list: + if package_name == package[2]: + return True + return False + def download_only(self, package_name, repo_url=None, repo_key=None): """ Similar to `apt-get install --download-only PACKAGE_NAME` diff --git a/wo/core/fileutils.py b/wo/core/fileutils.py index d1f1410..be676f0 100644 --- a/wo/core/fileutils.py +++ b/wo/core/fileutils.py @@ -252,10 +252,7 @@ class WOFileUtils(): Check if file exist on given path """ try: - if os.path.exists(path): - return (True) - else: - return (False) + return bool(os.path.exists(path)) except OSError as e: Log.debug(self, "{0}".format(e.strerror)) Log.error(self, "Unable to check path {0}".format(path)) @@ -369,3 +366,22 @@ class WOFileUtils(): except IOError as e: Log.debug(self, "{0}".format(e)) Log.error(self, "Unable to append content in {0}".format(path)) + + def enabledisable(self, path, enable=True): + """Switch conf from .conf.disabled to .conf or vice-versa""" + if enable: + Log.debug(self, "Check if disabled file exist") + if os.path.exists('{0}.disabled'.format(path)): + Log.debug(self, "Moving .disabled file") + shutil.move('{0}.disabled'.format(path), path) + return True + else: + return False + else: + Log.debug(self, "Check if .conf file exist") + if os.path.exists(path): + Log.debug(self, "Moving .conf file") + shutil.move(path, '{0}.disabled'.format(path)) + return True + else: + return False diff --git a/wo/core/git.py b/wo/core/git.py index fc36c0f..29ab1fc 100644 --- a/wo/core/git.py +++ b/wo/core/git.py @@ -18,25 +18,24 @@ class WOGit: """ for path in paths: global git - git = git.bake("--git-dir={0}/.git".format(path), - "--work-tree={0}".format(path)) + wogit = git.bake("-C", "{0}".format(path)) if os.path.isdir(path): if not os.path.isdir(path + "/.git"): try: Log.debug(self, "WOGit: git init at {0}" .format(path)) - git.init(path) + wogit.init(path) except ErrorReturnCode as e: Log.debug(self, "{0}".format(e)) Log.error(self, "Unable to git init at {0}" .format(path)) - status = git.status("-s") + status = wogit.status("-s") if len(status.splitlines()) > 0: try: Log.debug(self, "WOGit: git commit at {0}" .format(path)) - git.add("--all") - git.commit("-am {0}".format(msg)) + wogit.add("--all") + wogit.commit("-am {0}".format(msg)) except ErrorReturnCode as e: Log.debug(self, "{0}".format(e)) Log.error(self, "Unable to git commit at {0} " @@ -49,9 +48,8 @@ class WOGit: Checks status of file, If its tracked or untracked. """ global git - git = git.bake("--git-dir={0}/.git".format(repo), - "--work-tree={0}".format(repo)) - status = git.status("-s", "{0}".format(filepath)) + wogit = git.bake("-C", "{0}".format(repo)) + status = wogit.status("-s", "{0}".format(filepath)) if len(status.splitlines()) > 0: return True else: @@ -64,8 +62,7 @@ class WOGit: """ for path in paths: global git - git = git.bake("--git-dir={0}/.git".format(path), - "--work-tree={0}".format(path)) + wogit = git.bake("-C", "{0}".format(path)) if os.path.isdir(path): if not os.path.isdir(path + "/.git"): Log.error( @@ -75,8 +72,8 @@ class WOGit: Log.debug( self, "WOGit: git stash --include-untracked at {0}" .format(path)) - git.stash("push", "--include-untracked", "-m {0}" - .format(msg)) + wogit.stash("push", "--include-untracked", "-m {0}" + .format(msg)) except ErrorReturnCode as e: Log.debug(self, "{0}".format(e)) Log.error(self, "Unable to git reset at {0} " diff --git a/wo/core/nginx.py b/wo/core/nginx.py new file mode 100644 index 0000000..76e541a --- /dev/null +++ b/wo/core/nginx.py @@ -0,0 +1,19 @@ +"""WordOps Nginx Manager""" +import subprocess + +from wo.core.logging import Log + + +def check_config(self): + """Check Nginx configuration and return boolean""" + Log.debug(self, "Testing Nginx configuration ") + # Check Nginx configuration before executing command + sub = subprocess.Popen('nginx -t', stdout=subprocess.PIPE, + stderr=subprocess.PIPE, shell=True) + output, error_output = sub.communicate() + if 'emerg' in str(error_output): + Log.debug(self, "Nginx configuration check failed") + return False + else: + Log.debug(self, "Nginx configuration check was successful") + return True diff --git a/wo/core/services.py b/wo/core/services.py index 33ac4b6..84156aa 100644 --- a/wo/core/services.py +++ b/wo/core/services.py @@ -20,15 +20,17 @@ class WOService(): # Check Nginx configuration before executing command sub = subprocess.Popen('nginx -t', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) - output, error_output = sub.communicate() - if 'emerg' not in str(error_output): + output = sub.communicate() + if 'emerg' not in str(output): Log.valide(self, "Testing Nginx configuration ") - Log.wait(self, "Starting Nginx ") + Log.wait(self, "Starting Nginx") service_cmd = ('service {0} start'.format(service_name)) retcode = subprocess.getstatusoutput(service_cmd) if retcode[0] == 0: Log.valide(self, "Starting Nginx ") return True + else: + Log.failed(self, "Starting Nginx") else: Log.failed(self, "Testing Nginx configuration ") return False @@ -129,15 +131,15 @@ class WOService(): output, error_output = sub.communicate() if 'emerg' not in str(error_output): Log.valide(self, "Testing Nginx configuration ") - Log.wait(self, "Reloading Nginx ") + Log.wait(self, "Reloading Nginx") service_cmd = ('service {0} reload'.format(service_name)) retcode = subprocess.getstatusoutput(service_cmd) if retcode[0] == 0: - Log.valide(self, "Reloading Nginx ") + Log.valide(self, "Reloading Nginx") return True - else: - Log.failed(self, "Testing Nginx configuration ") - return False + else: + Log.failed(self, "Testing Nginx configuration ") + return False else: service_cmd = ('service {0} reload'.format(service_name)) Log.wait(self, "Reloading {0:10}".format( @@ -160,10 +162,11 @@ class WOService(): def get_service_status(self, service_name): try: - is_exist = subprocess.getstatusoutput('which {0}' + is_exist = subprocess.getstatusoutput('command -v {0}' .format(service_name)) if is_exist[0] == 0 or service_name in ['php7.2-fpm', - 'php7.3-fpm']: + 'php7.3-fpm', + 'php7.4-fpm']: retcode = subprocess.getstatusoutput('service {0} status' .format(service_name)) if retcode[0] == 0: diff --git a/wo/core/shellexec.py b/wo/core/shellexec.py index 38eeb42..3fb638c 100644 --- a/wo/core/shellexec.py +++ b/wo/core/shellexec.py @@ -37,27 +37,6 @@ class WOShellExec(): Log.debug(self, str(e)) raise CommandExecutionError - def cmd_exist(self, command): - """Check if a command exist with command -v""" - try: - Log.debug(self, "Testing command: {0}".format(command)) - testing_command = ("command -v {0}".format(command)) - with subprocess.Popen([testing_command], stdout=subprocess.PIPE, - stderr=subprocess.PIPE, shell=True) as proc: - (cmd_stdout_bytes, cmd_stderr_bytes) = proc.communicate() - (cmd_stdout, cmd_stderr) = ( - cmd_stdout_bytes.decode('utf-8', "replace"), - cmd_stderr_bytes.decode('utf-8', "replace")) - Log.debug(self, "Command Output: {0}, \nCommand Error: {1}" - .format(cmd_stdout, cmd_stderr)) - return bool(proc.returncode == 0) - except OSError as e: - Log.debug(self, str(e)) - raise CommandExecutionError - except Exception as e: - Log.debug(self, str(e)) - raise CommandExecutionError - def invoke_editor(self, filepath, errormsg=''): """ Open files using sensible editor diff --git a/wo/core/sslutils.py b/wo/core/sslutils.py index a63f975..2da6999 100644 --- a/wo/core/sslutils.py +++ b/wo/core/sslutils.py @@ -6,26 +6,39 @@ from wo.core.fileutils import WOFileUtils from wo.core.logging import Log from wo.core.shellexec import WOShellExec from wo.core.variables import WOVar +from wo.core.acme import WOAcme class SSL: def getexpirationdays(self, domain, returnonerror=False): # check if exist - if not os.path.isfile('/etc/letsencrypt/live/{0}/cert.pem' + if not os.path.exists('/etc/letsencrypt/live/{0}/cert.pem' .format(domain)): - Log.error(self, 'File Not Found: ' - '/etc/letsencrypt/live/{0}/cert.pem' - .format(domain), False) - if returnonerror: - return -1 - Log.error(self, "Check the WordOps log for more details " - "`tail /var/log/wo/wordops.log` and please try again...") + Log.debug(self, "cert not found for {0}".format(domain)) + split_domain = domain.split('.') + root_domain = ('.').join(split_domain[1:]) + + Log.debug(self, "trying with {0}".format(root_domain)) + if os.path.exists('/etc/letsencrypt/live/{0}/cert.pem' + .format(root_domain)): + domain = root_domain + else: + Log.error(self, 'File Not Found: ' + '/etc/letsencrypt/live/{0}/cert.pem' + .format(domain), False) + Log.error( + self, "Check the WordOps log for more details " + "`tail /var/log/wo/wordops.log` " + "and please try again...") + Log.debug( + self, + "Getting expiration of /etc/letsencrypt/live/{0}/cert.pem" + .format(domain)) current_date = WOShellExec.cmd_exec_stdout(self, "date -d \"now\" +%s") expiration_date = WOShellExec.cmd_exec_stdout( - self, "date -d \"" - "$(openssl x509 -in /etc/letsencrypt/live/" + self, "date -d \"$(openssl x509 -in /etc/letsencrypt/live/" "{0}/cert.pem -text -noout | grep \"Not After\" " "| cut -c 25-)\" +%s" .format(domain)) @@ -39,23 +52,27 @@ class SSL: def getexpirationdate(self, domain): # check if exist - if os.path.islink('/var/www/{0}/conf/nginx/ssl.conf'): - split_domain = domain.split('.') - domain = ('.').join(split_domain[1:]) if not os.path.isfile('/etc/letsencrypt/live/{0}/cert.pem' .format(domain)): - Log.error(self, 'File Not Found: /etc/letsencrypt/' - 'live/{0}/cert.pem' - .format(domain), False) - Log.error(self, "Check the WordOps log for more details " - "`tail /var/log/wo/wordops.log` and please try again...") + if os.path.exists('/var/www/{0}/conf/nginx/ssl.conf'): + split_domain = domain.split('.') + check_domain = ('.').join(split_domain[1:]) + else: + Log.error( + self, 'File Not Found: /etc/letsencrypt/' + 'live/{0}/cert.pem' + .format(domain), False) + Log.error( + self, "Check the WordOps log for more details " + "`tail /var/log/wo/wordops.log` and please try again...") + else: + check_domain = domain - expiration_date = WOShellExec.cmd_exec_stdout( + return WOShellExec.cmd_exec_stdout( self, "date -d \"$(/usr/bin/openssl x509 -in " "/etc/letsencrypt/live/{0}/cert.pem -text -noout | grep " "\"Not After\" | cut -c 25-)\" " - .format(domain)) - return expiration_date + .format(check_domain)) def siteurlhttps(self, domain): wo_site_webroot = ('/var/www/{0}'.format(domain)) @@ -93,8 +110,8 @@ class SSL: Log.valide(self, "Updating site url with https") # check if a wildcard exist to secure a new subdomain - def checkwildcardexist(self, wo_domain_name): + """Check if a wildcard certificate exist for a domain""" wo_acme_exec = ("/etc/letsencrypt/acme.sh --config-home " "'/etc/letsencrypt/config'") @@ -110,31 +127,44 @@ class SSL: reader = csv.reader(certfile, 'acmeconf') wo_wildcard_domain = ("*.{0}".format(wo_domain_name)) for row in reader: - if wo_wildcard_domain in row[2]: - if not row[2] == "": - iswildcard = True - break - else: - iswildcard = False + if wo_wildcard_domain == row[2]: + if not row[3] == "": + return True certfile.close() + return False - return iswildcard + def setuphsts(self, wo_domain_name, enable=True): + """Enable or disable htsts for a site""" + if enable: + if WOFileUtils.enabledisable( + self, '/var/www/{0}/conf/nginx/hsts.conf' + ): + return 0 + else: + Log.info( + self, "Adding /var/www/{0}/conf/nginx/hsts.conf" + .format(wo_domain_name)) - def setuphsts(self, wo_domain_name): - Log.info( - self, "Adding /var/www/{0}/conf/nginx/hsts.conf" - .format(wo_domain_name)) - - hstsconf = open("/var/www/{0}/conf/nginx/hsts.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - hstsconf.write("more_set_headers " - "\"Strict-Transport-Security: " - "max-age=31536000; " - "includeSubDomains; " - "preload\";") - hstsconf.close() - return 0 + hstsconf = open("/var/www/{0}/conf/nginx/hsts.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + hstsconf.write("more_set_headers " + "\"Strict-Transport-Security: " + "max-age=31536000; " + "includeSubDomains; " + "preload\";") + hstsconf.close() + return 0 + else: + if WOFileUtils.enabledisable( + self, '/var/www/{0}/conf/nginx/hsts.conf', + enable=False + ): + Log.info(self, "HSTS disabled") + return 0 + else: + Log.info(self, "HSTS is not enabled") + return 0 def selfsignedcert(self, proftpd=False, backend=False): """issue a self-signed certificate""" @@ -197,3 +227,100 @@ class SSL: "/etc/proftpd/ssl/proftpd.crt") # remove self-signed tmp directory WOFileUtils.rm(self, selfs_tmp) + + def httpsredirect(self, wo_domain_name, acme_domains, redirect=True): + """Create Nginx redirection from http to https""" + wo_acme_domains = ' '.join(acme_domains) + if redirect: + Log.wait(self, "Adding HTTPS redirection") + if WOFileUtils.enabledisable( + self, '/etc/nginx/conf.d/force-ssl-{0}.conf' + .format(wo_domain_name), enable=True): + Log.valide(self, "Adding HTTPS redirection") + return 0 + else: + try: + sslconf = open( + "/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + sslconf.write( + "server {\n" + "\tlisten 80;\n" + + "\tlisten [::]:80;\n" + + "\tserver_name {0};\n" + .format(wo_acme_domains) + + "\treturn 301 https://$host" + "$request_uri;\n}") + sslconf.close() + except IOError as e: + Log.debug(self, str(e)) + Log.debug( + self, "Error occured while generating " + "/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name)) + return 1 + Log.valide(self, "Adding HTTPS redirection") + return 0 + else: + if WOFileUtils.enabledisable( + self, "/etc/nginx/conf.d/force-ssl-{0}.conf" + .format(wo_domain_name), enable=False): + Log.info( + self, "Disabled HTTPS Force Redirection for site " + "{0}".format(wo_domain_name)) + else: + Log.info( + self, "HTTPS redirection already disabled for site" + "{0}".format(wo_domain_name) + ) + return 0 + + def archivedcertificatehandle(self, domain, acme_domains): + Log.warn( + self, "You already have an existing certificate " + "for the domain requested.\n" + "(ref: {0}/" + "{1}_ecc/{1}.conf)".format(WOVar.wo_ssl_archive, domain) + + "\nPlease select an option from below?" + "\n\t1: Reinstall existing certificate" + "\n\t2: Issue a new certificate to replace " + "the current one (limit ~5 per 7 days)" + "") + check_prompt = input( + "\nType the appropriate number [1-2] or any other key to cancel: ") + if not os.path.isfile("{0}/{1}/fullchain.pem" + .format(WOVar.wo_ssl_live, domain)): + Log.debug( + self, "{0}/{1}/fullchain.pem file is missing." + .format(WOVar.wo_ssl_live, domain)) + check_prompt = "2" + + if check_prompt == "1": + Log.info(self, "Reinstalling SSL cert with acme.sh") + ssl = WOAcme.deploycert(self, domain) + if ssl: + SSL.httpsredirect(self, domain, acme_domains) + + elif (check_prompt == "2"): + Log.info(self, "Issuing new SSL cert with acme.sh") + ssl = WOShellExec.cmd_exec( + self, "/etc/letsencrypt/acme.sh " + "--config-home '/etc/letsencrypt/config' " + "--renew -d {0} --ecc --force" + .format(domain)) + + if ssl: + WOAcme.deploycert(self, domain) + else: + Log.error(self, "Operation cancelled by user.") + + if os.path.isfile("{0}/conf/nginx/ssl.conf" + .format(domain)): + Log.info(self, "Existing ssl.conf . Backing it up ..") + WOFileUtils.mvfile(self, "/var/www/{0}/conf/nginx/ssl.conf" + .format(domain), + '/var/www/{0}/conf/nginx/ssl.conf.bak' + .format(domain)) + + return ssl diff --git a/wo/core/stackconf.py b/wo/core/stackconf.py new file mode 100644 index 0000000..dd0d5c7 --- /dev/null +++ b/wo/core/stackconf.py @@ -0,0 +1,50 @@ +import os + +from wo.core.logging import Log +from wo.core.template import WOTemplate +from wo.core.variables import WOVar + + +class WOConf(): + """wo stack configuration utilities""" + def __init__(): + pass + + def nginxcommon(self): + """nginx common configuration deployment""" + wo_php_version = ["php72", "php73", "php74"] + ngxcom = '/etc/nginx/common' + for wo_php in wo_php_version: + if not os.path.exists(ngxcom): + os.mkdir(ngxcom) + Log.debug(self, 'deploying templates for {0}'.format(wo_php)) + data = dict(upstream="{0}".format(wo_php), + release=WOVar.wo_version) + WOTemplate.deploy(self, + '{0}/{1}.conf' + .format(ngxcom, wo_php), + 'php.mustache', data) + + WOTemplate.deploy( + self, '{0}/redis-{1}.conf'.format(ngxcom, wo_php), + 'redis.mustache', data) + + WOTemplate.deploy( + self, '{0}/wpcommon-{1}.conf'.format(ngxcom, wo_php), + 'wpcommon.mustache', data) + + WOTemplate.deploy( + self, '{0}/wpfc-{1}.conf'.format(ngxcom, wo_php), + 'wpfc.mustache', data) + + WOTemplate.deploy( + self, '{0}/wpsc-{1}.conf'.format(ngxcom, wo_php), + 'wpsc.mustache', data) + + WOTemplate.deploy( + self, '{0}/wprocket-{1}.conf'.format(ngxcom, wo_php), + 'wprocket.mustache', data) + + WOTemplate.deploy( + self, '{0}/wpce-{1}.conf'.format(ngxcom, wo_php), + 'wpce.mustache', data) diff --git a/wo/core/variables.py b/wo/core/variables.py index dad38e2..d3d773a 100644 --- a/wo/core/variables.py +++ b/wo/core/variables.py @@ -14,11 +14,11 @@ class WOVar(): """Intialization of core variables""" # WordOps version - wo_version = "3.10.3" + wo_version = "3.11.0" # WordOps packages versions - wo_wp_cli = "2.3.0" - wo_adminer = "4.7.3" - wo_phpmyadmin = "4.9.1" + wo_wp_cli = "2.4.0" + wo_adminer = "4.7.5" + wo_phpmyadmin = "4.9.2" wo_extplorer = "2.1.13" wo_dashboard = "1.2" @@ -133,16 +133,23 @@ class WOVar(): wo_nginx = ["nginx-custom", "nginx-wo"] wo_nginx_key = '188C9FB063F0247A' - wo_php = ["php7.2-fpm", "php7.2-curl", "php7.2-gd", "php7.2-imap", - "php7.2-readline", "php7.2-common", "php7.2-recode", - "php7.2-cli", "php7.2-mbstring", "php7.2-intl", - "php7.2-bcmath", "php7.2-mysql", "php7.2-opcache", - "php7.2-zip", "php7.2-xml", "php7.2-soap"] - wo_php73 = ["php7.3-fpm", "php7.3-curl", "php7.3-gd", "php7.3-imap", - "php7.3-readline", "php7.3-common", "php7.3-recode", - "php7.3-cli", "php7.3-mbstring", "php7.3-intl", - "php7.3-bcmath", "php7.3-mysql", "php7.3-opcache", - "php7.3-zip", "php7.3-xml", "php7.3-soap"] + wo_module = ["fpm", "curl", "gd", "imap", + "readline", "common", + "cli", "mbstring", "intl", + "bcmath", "mysql", "opcache", + "zip", "xml", "soap"] + wo_php72 = [] + for module in wo_module: + wo_php72 = wo_php72 + ["php7.2-{0}".format(module), + "php7.2-recode"] + wo_php73 = [] + for module in wo_module: + wo_php73 = wo_php73 + ["php7.3-{0}".format(module), + "php7.3-recode"] + wo_php74 = [] + for module in wo_module: + wo_php74 = wo_php74 + ["php7.4-{0}".format(module)] + wo_php_extra = ["php-memcached", "php-imagick", "graphviz", "php-xdebug", "php-msgpack", "php-redis"] diff --git a/wo/core/wpcli.py b/wo/core/wpcli.py new file mode 100644 index 0000000..da2ac85 --- /dev/null +++ b/wo/core/wpcli.py @@ -0,0 +1,17 @@ +"""WordPress utilities for WordOps""" +from wo.core.logging import Log +from wo.core.shellexec import WOShellExec +from wo.core.variables import WOVar + + +class WOWp: + """WordPress utilities for WordOps""" + + def wpcli(self, command): + """WP-CLI wrapper""" + try: + WOShellExec.cmd_exec( + self, '{0} --allow-root '.format(WOVar.wo_wpcli_path) + + '{0}'.format(command)) + except Exception: + Log.error(self, "WP-CLI command failed")