Add --lint flag for the 'deploy' action and a 'lint' action to validate the Docker compose/stack file

For this (optional) new feature, the 'docker-compose' tool must be installed,
see: https://docs.docker.com/compose/install/
This commit is contained in:
Tortue Torche
2019-08-11 02:30:47 -04:00
committed by Tortue Torche
parent a494dcbb79
commit c0a98d6505

58
psu
View File

@ -48,6 +48,7 @@ main() {
"masked-variables;-m;--masked-variables;In debug and/or verbose mode, the value of sensitive variables will be hidden, useful in CI to avoid leaking passwords/tokens in logs. Possible values: true|extended|false. Defaults to false" "masked-variables;-m;--masked-variables;In debug and/or verbose mode, the value of sensitive variables will be hidden, useful in CI to avoid leaking passwords/tokens in logs. Possible values: true|extended|false. Defaults to false"
"quiet;-q;--quiet;Display the minimum of information or nothing, UNIX/Linux friendly. Defaults to false" "quiet;-q;--quiet;Display the minimum of information or nothing, UNIX/Linux friendly. Defaults to false"
"strict;-t;--strict;Never updates an existent stack nor removes an inexistent one, and instead exits with an error. Defaults to false" "strict;-t;--strict;Never updates an existent stack nor removes an inexistent one, and instead exits with an error. Defaults to false"
"lint;-L;--lint;Validate the Docker compose/stack file before deploying the stack (only used when action=deploy). Defaults to false"
"help;-h;--help;Display help message. To display help of a given action, run: 'psu <action> --help'" "help;-h;--help;Display help message. To display help of a given action, run: 'psu <action> --help'"
"version;-V;--version;Display the version of this program" "version;-V;--version;Display the version of this program"
"secure;-s;--secure[=yes|no];DEPRECATED: Use the '--insecure' option instead. Enable or disable the host's SSL certificate verification. Defaults to 'yes'" "secure;-s;--secure[=yes|no];DEPRECATED: Use the '--insecure' option instead. Enable or disable the host's SSL certificate verification. Defaults to 'yes'"
@ -56,7 +57,7 @@ main() {
ACTIONS_TABLE=( ACTIONS_TABLE=(
# action_name;description[;required_option_key1|required_option_key2...][;optional_option_key1|optional_option_key2...] # action_name;description[;required_option_key1|required_option_key2...][;optional_option_key1|optional_option_key2...]
"deploy;Deploy the stack;url|user|password|name|compose-file;auth-token|endpoint|env-file|prune|insecure|verbose|debug|masked-variables|strict" "deploy;Deploy the stack;url|user|password|name|compose-file;auth-token|endpoint|lint|env-file|prune|insecure|verbose|debug|masked-variables|strict"
"undeploy;Undeploy/remove the stack;url|user|password|name;auth-token|endpoint|insecure|verbose|debug|masked-variables|strict" "undeploy;Undeploy/remove the stack;url|user|password|name;auth-token|endpoint|insecure|verbose|debug|masked-variables|strict"
"login;Log in to a Portainer instance;url|user|password;insecure|verbose|debug|masked-variables" "login;Log in to a Portainer instance;url|user|password;insecure|verbose|debug|masked-variables"
"list;Lists of the stacks already deployed;url|user|password;auth-token|endpoint|quiet|insecure|verbose|debug|masked-variables|help" "list;Lists of the stacks already deployed;url|user|password;auth-token|endpoint|quiet|insecure|verbose|debug|masked-variables|help"
@ -67,14 +68,15 @@ main() {
"tasks;Lists tasks for the current stack;url|user|password|name;auth-token|endpoint|service|detect-job|timeout|quiet|insecure|verbose|debug|masked-variables" "tasks;Lists tasks for the current stack;url|user|password|name;auth-token|endpoint|service|detect-job|timeout|quiet|insecure|verbose|debug|masked-variables"
"tasks:healthy;Lists tasks who are running correctly for the current stack;url|user|password|name;auth-token|endpoint|service|detect-job|timeout|quiet|insecure|verbose|debug|masked-variables" "tasks:healthy;Lists tasks who are running correctly for the current stack;url|user|password|name;auth-token|endpoint|service|detect-job|timeout|quiet|insecure|verbose|debug|masked-variables"
"containers;Lists containers running for the current stack;url|user|password|name;auth-token|endpoint|service|quiet|insecure|verbose|debug|masked-variables" "containers;Lists containers running for the current stack;url|user|password|name;auth-token|endpoint|service|quiet|insecure|verbose|debug|masked-variables"
"actions;Lists available actions of this program;;verbose|debug|masked-variables" "lint;Validate the Docker compose/stack file;compose-file;verbose|debug"
"actions;Lists available actions of this program;;verbose|debug"
"help;Display help message" "help;Display help message"
"version;Display this program version" "version;Display this program version"
) )
# Special actions who display text only # Special actions who display text only
# No HTTP requests are done # No HTTP requests will be made
ACTIONS_TEXT_ONLY="actions help version" ACTIONS_TEXT_ONLY="actions lint help version"
# Aliases of default actions # Aliases of default actions
# NOTICE: This is an experimental feature # NOTICE: This is an experimental feature
@ -325,6 +327,12 @@ main() {
exit 0 exit 0
fi fi
# Lint the Docker compose/stack file
if [ $ACTION == "lint" ]; then
lint
exit 0
fi
# Get list of all actions who can be used for this program # Get list of all actions who can be used for this program
if [ $ACTION == "actions" ]; then if [ $ACTION == "actions" ]; then
echo "Portainer Stack Utils, version $VERSION" echo "Portainer Stack Utils, version $VERSION"
@ -350,6 +358,7 @@ main() {
# PORTAINER_STACK_NAME # # PORTAINER_STACK_NAME #
# PORTAINER_SERVICE_NAME # # PORTAINER_SERVICE_NAME #
# DOCKER_COMPOSE_FILE # # DOCKER_COMPOSE_FILE #
# DOCKER_COMPOSE_LINT #
# ENVIRONMENT_VARIABLES_FILE # # ENVIRONMENT_VARIABLES_FILE #
# PORTAINER_ENDPOINT # # PORTAINER_ENDPOINT #
# PORTAINER_PRUNE # # PORTAINER_PRUNE #
@ -378,6 +387,7 @@ set_globals() {
PORTAINER_STACK_NAME=${PORTAINER_STACK_NAME} PORTAINER_STACK_NAME=${PORTAINER_STACK_NAME}
PORTAINER_SERVICE_NAME=${PORTAINER_SERVICE_NAME} PORTAINER_SERVICE_NAME=${PORTAINER_SERVICE_NAME}
DOCKER_COMPOSE_FILE=${DOCKER_COMPOSE_FILE} DOCKER_COMPOSE_FILE=${DOCKER_COMPOSE_FILE}
DOCKER_COMPOSE_LINT=${DOCKER_COMPOSE_LINT:-"false"}
ENVIRONMENT_VARIABLES_FILE=${ENVIRONMENT_VARIABLES_FILE} ENVIRONMENT_VARIABLES_FILE=${ENVIRONMENT_VARIABLES_FILE}
PORTAINER_ENDPOINT=${PORTAINER_ENDPOINT:-"1"} PORTAINER_ENDPOINT=${PORTAINER_ENDPOINT:-"1"}
PORTAINER_PRUNE=${PORTAINER_PRUNE:-"false"} PORTAINER_PRUNE=${PORTAINER_PRUNE:-"false"}
@ -402,6 +412,7 @@ set_globals() {
echo_debug "PORTAINER_STACK_NAME -> $PORTAINER_STACK_NAME" echo_debug "PORTAINER_STACK_NAME -> $PORTAINER_STACK_NAME"
echo_debug "PORTAINER_SERVICE_NAME -> $PORTAINER_SERVICE_NAME" echo_debug "PORTAINER_SERVICE_NAME -> $PORTAINER_SERVICE_NAME"
echo_debug "DOCKER_COMPOSE_FILE -> $DOCKER_COMPOSE_FILE" echo_debug "DOCKER_COMPOSE_FILE -> $DOCKER_COMPOSE_FILE"
echo_debug "DOCKER_COMPOSE_LINT -> $DOCKER_COMPOSE_LINT"
echo_debug "ENVIRONMENT_VARIABLES_FILE -> $ENVIRONMENT_VARIABLES_FILE" echo_debug "ENVIRONMENT_VARIABLES_FILE -> $ENVIRONMENT_VARIABLES_FILE"
echo_debug "PORTAINER_ENDPOINT -> $PORTAINER_ENDPOINT" echo_debug "PORTAINER_ENDPOINT -> $PORTAINER_ENDPOINT"
echo_debug "PORTAINER_PRUNE -> $PORTAINER_PRUNE" echo_debug "PORTAINER_PRUNE -> $PORTAINER_PRUNE"
@ -423,8 +434,8 @@ set_globals() {
fi fi
check_argument "$PORTAINER_URL" "portainer url" "PORTAINER_URL" "l" "url" check_argument "$PORTAINER_URL" "portainer url" "PORTAINER_URL" "l" "url"
fi fi
if [ "$ACTION" == "deploy" ]; then if [ "$ACTION" == "deploy" ] || [ "$ACTION" == "lint" ]; then
check_argument "$DOCKER_COMPOSE_FILE" "docker stack file" "DOCKER_COMPOSE_FILE" "f" "stack-file" check_argument "$DOCKER_COMPOSE_FILE" "docker compose file" "DOCKER_COMPOSE_FILE" "c" "compose-file"
if [ -n "$ENVIRONMENT_VARIABLES_FILE" ] && [[ ! -f "$ENVIRONMENT_VARIABLES_FILE" ]]; then if [ -n "$ENVIRONMENT_VARIABLES_FILE" ] && [[ ! -f "$ENVIRONMENT_VARIABLES_FILE" ]]; then
echo_error "Error: File path \"$ENVIRONMENT_VARIABLES_FILE\" not found for \"ENVIRONMENT_VARIABLES_FILE\" environment variable or the \"-g\" flag or the \"--env-file\" option." echo_error "Error: File path \"$ENVIRONMENT_VARIABLES_FILE\" not found for \"ENVIRONMENT_VARIABLES_FILE\" environment variable or the \"-g\" flag or the \"--env-file\" option."
exit 1 exit 1
@ -488,6 +499,13 @@ inputs() {
shift shift
fi fi
;; ;;
-L|--lint|-L=*|--lint=*)
DOCKER_COMPOSE_LINT=$(input_flag "$1" "$2")
if [ -n "$2" ] && [[ ! $2 =~ ^-.+$ ]] ; then
# When the second argument is the value of the current option
shift
fi
;;
-g=*|--env-file=*|-g|--env-file) -g=*|--env-file=*|-g|--env-file)
ENVIRONMENT_VARIABLES_FILE=$(input_option "$1" "$2") ENVIRONMENT_VARIABLES_FILE=$(input_option "$1" "$2")
if [ -n "$2" ] && [[ ! $2 =~ ^-.+$ ]] ; then if [ -n "$2" ] && [[ ! $2 =~ ^-.+$ ]] ; then
@ -910,6 +928,7 @@ mask_variables() {
# Globals: # # Globals: #
# STACK # # STACK #
# DOCKER_COMPOSE_FILE # # DOCKER_COMPOSE_FILE #
# DOCKER_COMPOSE_LINT #
# PORTAINER_STACK_NAME # # PORTAINER_STACK_NAME #
# PORTAINER_URL # # PORTAINER_URL #
# ENVIRONMENT_VARIABLES_FILE # # ENVIRONMENT_VARIABLES_FILE #
@ -922,6 +941,10 @@ mask_variables() {
# None # # None #
################################ ################################
deploy() { deploy() {
# Lint docker-compose file if --lint option is "true"
if [ "$DOCKER_COMPOSE_LINT" == "true" ]; then
lint
fi
# Read docker-compose file content # Read docker-compose file content
local docker_compose_file_content local docker_compose_file_content
docker_compose_file_content="$( jq -Rscjr '{StackFileContent: . }' $DOCKER_COMPOSE_FILE | tail -c +2 | head -c -1 )" docker_compose_file_content="$( jq -Rscjr '{StackFileContent: . }' $DOCKER_COMPOSE_FILE | tail -c +2 | head -c -1 )"
@ -1236,6 +1259,29 @@ containers() {
echo "$containers" echo "$containers"
} }
lint() {
local docker_stack_error
local docker_stack_validation
if [ -x "$(command -v docker-compose)" ]; then
echo_verbose "Linting Docker compose/stack file..."
docker_stack_error="error_docker_stack_is_invalid"
docker_stack_validation=$(docker-compose -f "$DOCKER_COMPOSE_FILE" config -q > docker_stack_validation_report 2>&1 || echo $docker_stack_error)
if [[ $docker_stack_validation =~ $docker_stack_error$ ]]; then
echo_error "Error: The '$DOCKER_COMPOSE_FILE' Docker compose/stack file is invalid:"
echo_error "$(cat docker_stack_validation_report)"
rm -f docker_stack_validation_report
exit 1
else
echo "[OK]"
echo_verbose "The '$DOCKER_COMPOSE_FILE' Docker compose/stack file is valid"
fi
else
echo "WARNING: Cannot validate '$DOCKER_COMPOSE_FILE' file, 'docker-compose' is not installed or not executable."
echo_verbose "For more informations, see: https://docs.docker.com/compose/install/"
fi || true
}
display_options_message() { display_options_message() {
echo "Options:" echo "Options:"
local table local table