feat: Pull invalid nginx configuration when the nginx configs fail to validate

- expose an nginx:validate command that can be used to show validation information
- properly reference the invalid nginx config, rather than tell the user that another app's nginx config is invalid when deploying their own app
- allow a user to cleanup bad nginx config files out of band when they are blocking a deploy _without_ requiring knowing where that nginx config is

Note that this may have issues with generated nginx.conf files that depend on other apps or other parts of the nginx config. For now, this is acceptable, though it is something that needs to be field tested.

Closes #3162
This commit is contained in:
Jose Diaz-Gonzalez
2018-12-30 08:33:53 -05:00
parent 7ad493c263
commit 0909052816
7 changed files with 164 additions and 17 deletions

View File

@@ -6,6 +6,7 @@ Dokku uses nginx as its server for routing requests to specific applications. By
nginx:access-logs <app> [-t] # Show the nginx access logs for an application (-t follows)
nginx:build-config <app> # (Re)builds nginx config for given app
nginx:error-logs <app> [-t] # Show the nginx error logs for an application (-t follows)
nginx:validate [<app>] [--clean] # Validates and optionally cleans up invalid nginx configurations
```
## Checking access logs
@@ -44,6 +45,30 @@ In certain cases, your app nginx configs may drift from the correct config for y
dokku nginx:build-config node-js-app
```
## Validating nginx configs
It may be desired to validate an nginx config outside of the deployment process. To do so, run the `nginx:validate` command. With no arguments, this will validate all app nginx configs, one at a time. A minimal wrapper nginx config is generated for each app's nginx config, upon which `nginx -t` will be run.
```shell
dokku nginx:validate
```
As app nginx configs are actually executed within a shared context, it is possible for an individual config to be invalid when being validated standalone but _also_ be valid within the global server context. As such, the exit code for the `nginx:validate` command is the exit code of `nginx -t` against the server's real nginx config.
The `nginx:validate` command also takes an optional `--clean` flag. If specified, invalid nginx configs will be removed.
> Warning: Invalid app nginx config's will be removed _even if_ the config is valid in the global server context.
```shell
dokku nginx:validate --clean
```
The `--clean` flag may also be specified for a given app:
```shell
dokku nginx:validate node-js-app --clean
```
## Customizing the nginx configuration
> New as of 0.5.0

View File

@@ -6,6 +6,7 @@ source "$PLUGIN_AVAILABLE_PATH/config/functions"
source "$PLUGIN_AVAILABLE_PATH/domains/functions"
source "$PLUGIN_AVAILABLE_PATH/proxy/functions"
source "$PLUGIN_AVAILABLE_PATH/ps/functions"
source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/internal-functions"
get_nginx_location() {
declare desc="check that nginx is at the expected location and return it"
@@ -25,26 +26,45 @@ get_nginx_location() {
validate_nginx() {
declare desc="validate entire nginx config"
local NGINX_LOCATION
declare APP="${1:-}" FLAG="${2:-}"
local NGINX_LOCATION EXIT_CODE
NGINX_LOCATION=$(get_nginx_location)
if [[ -z "$NGINX_LOCATION" ]]; then
exit 1;
fi
if [[ "$APP" == "--clean" ]]; then
APP=""
FLAG="--clean"
fi
set +e
sudo "$NGINX_LOCATION" -t > /dev/null 2>&1
local exit_code=$?
EXIT_CODE=$?
set -e
if [[ "$exit_code" -ne "0" ]]; then
sudo "$NGINX_LOCATION" -t
shopt -s nullglob
local conf_file
for conf_file in $DOKKU_ROOT/*/nginx.conf; do
dokku_log_verbose "validate_nginx failed. contents of $conf_file below..."
cat "$conf_file"
done
exit "$exit_code"
if [[ "$EXIT_CODE" -eq "0" ]]; then
return
fi
if [[ -n "$APP" ]]; then
nginx_vhosts_validate_single_func "$APP" "$FLAG"
else
for app in $(dokku_apps); do
nginx_vhosts_validate_single_func "$app" "$FLAG"
done
fi
set +e
sudo "$NGINX_LOCATION" -t > /dev/null 2>&1
EXIT_CODE=$?
set -e
if [[ "$EXIT_CODE" -eq "0" ]]; then
return
fi
sudo "$NGINX_LOCATION" -t
exit $?
}
restart_nginx() {
@@ -320,7 +340,7 @@ nginx_build_config() {
PROXY_PORT_MAP=$(echo "$PROXY_PORT_MAP" | xargs) # trailing spaces mess up default template
eval "$(config_export app "$APP")"
local SIGIL_PARAMS=(-f $NGINX_TEMPLATE APP="$APP" DOKKU_ROOT="$DOKKU_ROOT"
local SIGIL_PARAMS=(-f "$NGINX_TEMPLATE" APP="$APP" DOKKU_ROOT="$DOKKU_ROOT"
NOSSL_SERVER_NAME="$NOSSL_SERVER_NAME"
DOKKU_APP_LISTENERS="$DOKKU_APP_LISTENERS"
DOKKU_LIB_ROOT="$DOKKU_LIB_ROOT"

View File

@@ -5,23 +5,23 @@ source "$PLUGIN_AVAILABLE_PATH/config/functions"
case "$DOKKU_DISTRO" in
debian)
echo "%dokku ALL=(ALL) NOPASSWD:/usr/sbin/invoke-rc.d nginx reload, /usr/sbin/nginx -t" > /etc/sudoers.d/dokku-nginx
echo "%dokku ALL=(ALL) NOPASSWD:/usr/sbin/invoke-rc.d nginx reload, /usr/sbin/nginx -t, /usr/sbin/nginx -t -c *" > /etc/sudoers.d/dokku-nginx
;;
ubuntu)
echo "%dokku ALL=(ALL) NOPASSWD:/etc/init.d/nginx reload, /usr/sbin/nginx -t" > /etc/sudoers.d/dokku-nginx
echo "%dokku ALL=(ALL) NOPASSWD:/etc/init.d/nginx reload, /usr/sbin/nginx -t, /usr/sbin/nginx -t -c *" > /etc/sudoers.d/dokku-nginx
;;
opensuse)
echo "%dokku ALL=(ALL) NOPASSWD:/sbin/service nginx reload, /usr/sbin/nginx -t" > /etc/sudoers.d/dokku-nginx
echo "%dokku ALL=(ALL) NOPASSWD:/sbin/service nginx reload, /usr/sbin/nginx -t, /usr/sbin/nginx -t -c *" > /etc/sudoers.d/dokku-nginx
;;
arch)
echo "%dokku ALL=(ALL) NOPASSWD:/usr/bin/systemctl reload nginx, /usr/sbin/nginx -t" > /etc/sudoers.d/dokku-nginx
echo "%dokku ALL=(ALL) NOPASSWD:/usr/bin/systemctl reload nginx, /usr/sbin/nginx -t, /usr/sbin/nginx -t -c *" > /etc/sudoers.d/dokku-nginx
;;
centos|rhel)
echo "%dokku ALL=(ALL) NOPASSWD:/usr/bin/systemctl reload nginx, /usr/sbin/nginx -t" > /etc/sudoers.d/dokku-nginx
echo "%dokku ALL=(ALL) NOPASSWD:/usr/bin/systemctl reload nginx, /usr/sbin/nginx -t, /usr/sbin/nginx -t -c *" > /etc/sudoers.d/dokku-nginx
echo "Defaults:dokku !requiretty" >> /etc/sudoers.d/dokku-nginx
;;
esac

View File

@@ -1,12 +1,52 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
nginx_vhosts_validate_single_func() {
declare APP="$1" FLAG="$2"
local NGINX_CONF="$DOKKU_ROOT/$APP/nginx.conf"
if [[ ! -f "$NGINX_CONF" ]]; then
dokku_log_warn_quiet "No nginx config found for ${APP}"
return
fi
if nginx_vhosts_is_valid_nginx_config_func "$APP"; then
return
fi
dokku_log_warn "Failed to validate nginx config for ${APP}. Contents below..."
cat "$NGINX_CONF"
if [[ "$FLAG" == "--clean" ]]; then
nginx_vhosts_conf_clean_func "$APP"
fi
}
nginx_vhosts_is_valid_nginx_config_func() {
declare desc="checks if an app has a valid nginx config"
declare APP="$1"
local VALIDATE_TEMPLATE="$PLUGIN_AVAILABLE_PATH/nginx-vhosts/templates/validate.conf.sigil"
local TMP_OUTPUT=$(mktemp "/tmp/${FUNCNAME[0]}.XXXX")
trap 'rm -rf "$TMP_OUTPUT" > /dev/null' RETURN INT TERM EXIT
sigil -f "$VALIDATE_TEMPLATE" NGINX_CONF="$DOKKU_ROOT/$APP/nginx.conf" | cat -s > "$TMP_OUTPUT"
sudo "$NGINX_LOCATION" -t -c "$TMP_OUTPUT" 2> /dev/null
}
nginx_vhosts_conf_clean_func() {
declare APP="$1"
local NGINX_CONF="$DOKKU_ROOT/$APP/nginx.conf"
dokku_log_warn "Removing invalid nginx file"
rm -f "$NGINX_CONF"
}
nginx_vhosts_help_content_func() {
declare desc="return nginx plugin help content"
cat<<help_content
nginx:build-config <app>, (Re)builds nginx config for given app
nginx:access-logs <app> [-t], Show the nginx access logs for an application (-t follows)
nginx:error-logs <app> [-t], Show the nginx error logs for an application (-t follows)
nginx:validate [<app>] [--clean], Validates and optionally cleans up invalid nginx configurations
help_content
}

View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
source "$PLUGIN_AVAILABLE_PATH/config/functions"
source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions"
nginx_validate_cmd() {
declare desc="validates and optionally cleans up invalid nginx configurations"
declare cmd="nginx:validate" argv=("$@"); [[ ${argv[0]} == "$cmd" ]] && shift 1
validate_nginx "$@"
}
nginx_validate_cmd "$@"

View File

@@ -0,0 +1,2 @@
events { worker_connections 768; }
http { include {{ $.NGINX_CONF }}; }

View File

@@ -106,3 +106,50 @@ assert_error_log() {
echo "status: $status"
assert_failure
}
@test "(nginx-vhosts) nginx:validate" {
deploy_app
run /bin/bash -c "dokku nginx:validate"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku nginx:validate $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
echo "invalid config" > "/home/dokku/${TEST_APP}/nginx.conf"
run /bin/bash -c "dokku nginx:validate"
echo "output: $output"
echo "status: $status"
assert_failure
run /bin/bash -c "dokku nginx:validate $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_failure
run /bin/bash -c "dokku nginx:validate --clean"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku nginx:validate"
echo "output: $output"
echo "status: $status"
assert_success
echo "invalid config" > "/home/dokku/${TEST_APP}/nginx.conf"
run /bin/bash -c "dokku nginx:validate $TEST_APP --clean"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku nginx:validate"
echo "output: $output"
echo "status: $status"
assert_success
}