2025-02-20 21:42:11 +01:00
#!/usr/bin/env sh
2025-02-21 19:02:55 +01:00
################################################################################
# ACME.sh 3rd party deploy plugin for multiple (same) services
################################################################################
# Authors: tomo2403 (creator), https://github.com/tomo2403
2025-03-01 13:26:53 +01:00
# Updated: 2025-03-01
# Issues: https://github.com/acmesh-official/acme.sh/issues and mention @tomo2403
2025-02-21 19:02:55 +01:00
################################################################################
# Usage (shown values are the examples):
# 1. Set optional environment variables
2025-03-27 19:11:31 +01:00
# - export MULTIDEPLOY_FILENAME="multideploy.yaml" - "multideploy.yml" will be automatically used if not set"
2025-02-21 19:02:55 +01:00
#
# 2. Run command:
# acme.sh --deploy --deploy-hook multideploy -d example.com
################################################################################
# Dependencies:
# - yq
################################################################################
# Return value:
# 0 means success, otherwise error.
################################################################################
2025-02-20 21:42:11 +01:00
MULTIDEPLOY_VERSION = "1.0"
2025-02-21 18:44:46 +01:00
# Description: This function handles the deployment of certificates to multiple services.
# It processes the provided certificate files and deploys them according to the
2025-03-27 19:11:31 +01:00
# configuration specified in the multideploy file.
2025-02-21 18:44:46 +01:00
#
# Parameters:
# _cdomain - The domain name for which the certificate is issued.
# _ckey - The private key file for the certificate.
# _ccert - The certificate file.
# _cca - The CA (Certificate Authority) file.
# _cfullchain - The full chain certificate file.
# _cpfx - The PFX (Personal Information Exchange) file.
2025-02-20 21:42:11 +01:00
multideploy_deploy( ) {
2025-02-20 21:56:28 +01:00
_cdomain = " $1 "
_ckey = " $2 "
_ccert = " $3 "
_cca = " $4 "
_cfullchain = " $5 "
_cpfx = " $6 "
_debug _cdomain " $_cdomain "
_debug _ckey " $_ckey "
_debug _ccert " $_ccert "
_debug _cca " $_cca "
_debug _cfullchain " $_cfullchain "
_debug _cpfx " $_cpfx "
2025-03-27 19:11:31 +01:00
MULTIDEPLOY_FILENAME = " ${ MULTIDEPLOY_FILENAME :- $( _getdeployconf MULTIDEPLOY_FILENAME) } "
if [ -z " $MULTIDEPLOY_FILENAME " ] ; then
MULTIDEPLOY_FILENAME = "multideploy.yml"
_info "MULTIDEPLOY_FILENAME is not set, so I will use 'multideploy.yml'."
2025-02-20 21:56:28 +01:00
else
2025-03-27 19:11:31 +01:00
_savedeployconf "MULTIDEPLOY_FILENAME" " $MULTIDEPLOY_FILENAME "
_debug2 "MULTIDEPLOY_FILENAME" " $MULTIDEPLOY_FILENAME "
2025-02-20 21:56:28 +01:00
fi
2025-03-27 19:11:31 +01:00
if ! file = $( _preprocess_deployfile " $MULTIDEPLOY_FILENAME " ) ; then
2025-02-21 18:44:46 +01:00
_err "Failed to preprocess deploy file."
return 1
fi
2025-02-21 18:23:13 +01:00
_debug3 "File" " $file "
2025-02-21 10:50:35 +01:00
# Deploy to services
2025-05-28 20:19:18 +02:00
_deploy_services " $file "
_exitCode = " $? "
2025-02-21 10:50:35 +01:00
2025-05-28 20:19:18 +02:00
return " $_exitCode "
2025-02-20 21:42:11 +01:00
}
2025-02-21 18:44:46 +01:00
# Description:
# This function preprocesses the deploy file by checking if 'yq' is installed,
# verifying the existence of the deploy file, and ensuring only one deploy file is present.
# Arguments:
# $@ - Posible deploy file names.
# Usage:
2025-03-27 19:11:31 +01:00
# _preprocess_deployfile "<deploy_file1>" "<deploy_file2>?"
2025-02-20 21:42:11 +01:00
_preprocess_deployfile( ) {
2025-02-20 21:56:28 +01:00
# Check if yq is installed
if ! command -v yq >/dev/null 2>& 1; then
_err "yq is not installed! Please install yq and try again."
return 1
fi
2025-02-21 15:51:02 +01:00
_debug3 "yq is installed."
# Check if deploy file exists
for file in " $@ " ; do
_debug3 "Checking file" " $DOMAIN_PATH / $file "
if [ -f " $DOMAIN_PATH / $file " ] ; then
_debug3 "File found"
if [ -n " $found_file " ] ; then
_err "Multiple deploy files found. Please keep only one deploy file."
return 1
fi
found_file = " $file "
else
_debug3 "File not found"
fi
done
2025-09-06 12:31:56 +02:00
if [ -z " $found_file " ] ; then
2025-02-21 19:26:56 +01:00
_err "Deploy file not found. Go to https://github.com/acmesh-official/acme.sh/wiki/deployhooks#36-deploying-to-multiple-services-with-the-same-hooks to see how to create one."
2025-02-20 21:56:28 +01:00
return 1
fi
2025-08-31 20:20:38 +02:00
if ! _check_deployfile " $DOMAIN_PATH / $found_file " ; then
_err " Deploy file is not valid: $DOMAIN_PATH / $found_file "
return 1
fi
2025-02-21 18:23:13 +01:00
echo " $DOMAIN_PATH / $found_file "
2025-02-20 21:42:11 +01:00
}
2025-02-21 18:44:46 +01:00
# Description:
# This function checks the deploy file for version compatibility and the existence of the specified configuration and services.
# Arguments:
# $1 - The path to the deploy configuration file.
# $2 - The name of the deploy configuration to use.
# Usage:
2025-03-27 19:11:31 +01:00
# _check_deployfile "<deploy_file_path>"
2025-02-20 21:42:11 +01:00
_check_deployfile( ) {
2025-02-21 10:49:08 +01:00
_deploy_file = " $1 "
2025-08-31 20:19:44 +02:00
_debug2 "check: Deploy file" " $_deploy_file "
2025-02-20 21:56:28 +01:00
# Check version
2025-12-20 17:04:04 +01:00
_deploy_file_version = $( yq -r '.version' " $_deploy_file " )
2025-02-21 10:49:08 +01:00
if [ " $MULTIDEPLOY_VERSION " != " $_deploy_file_version " ] ; then
_err " As of $PROJECT_NAME $VER , the deploy file needs version $MULTIDEPLOY_VERSION ! Your current deploy file is of version $_deploy_file_version . "
2025-02-20 21:56:28 +01:00
return 1
fi
2025-08-31 20:19:44 +02:00
_debug2 " check: Deploy file version is compatible: $_deploy_file_version "
2025-02-20 21:56:28 +01:00
# Extract all services from config
2025-12-20 17:04:04 +01:00
_services = $( yq -r '.services[].name' " $_deploy_file " )
2025-02-20 21:56:28 +01:00
2025-02-21 10:49:08 +01:00
if [ -z " $_services " ] ; then
2025-03-27 19:11:31 +01:00
_err "Config does not have any services to deploy to."
2025-02-20 21:56:28 +01:00
return 1
fi
2025-08-31 20:19:44 +02:00
_debug2 "check: Config has services."
echo " $_services " | while read -r _service; do
_debug3 " - $_service "
done
2025-02-20 21:56:28 +01:00
# Check if extracted services exist in services list
2025-08-31 14:45:34 +02:00
echo " $_services " | while read -r _service; do
2025-08-31 20:19:44 +02:00
_debug2 " check: Checking service: $_service "
2025-02-21 15:52:02 +01:00
# Check if service exists
2025-12-20 17:04:04 +01:00
_service_config = $( yq -r " .services[] | select(.name == \" $_service \") " " $_deploy_file " )
2025-08-31 20:19:44 +02:00
if [ -z " $_service_config " ] || [ " $_service_config " = "null" ] ; then
2025-02-21 10:49:08 +01:00
_err " Service ' $_service ' not found. "
2025-02-20 21:56:28 +01:00
return 1
2025-02-20 21:42:11 +01:00
fi
2025-12-20 17:04:04 +01:00
_service_hook = $( echo " $_service_config " | yq -r ".hook" -)
2025-08-31 20:19:44 +02:00
if [ -z " $_service_hook " ] || [ " $_service_hook " = "null" ] ; then
2025-02-21 10:49:08 +01:00
_err " Service ' $_service ' does not have a hook. "
2025-02-20 21:56:28 +01:00
return 1
2025-02-20 21:42:11 +01:00
fi
2025-12-20 17:04:04 +01:00
_service_environment = $( echo " $_service_config " | yq -r ".environment" -)
2025-08-31 20:19:44 +02:00
if [ -z " $_service_environment " ] || [ " $_service_environment " = "null" ] ; then
2025-02-21 18:44:46 +01:00
_err " Service ' $_service ' does not have an environment. "
2025-02-20 21:56:28 +01:00
return 1
2025-02-20 21:42:11 +01:00
fi
2025-02-20 21:56:28 +01:00
done
2025-02-20 21:42:11 +01:00
}
2025-02-20 23:02:56 +01:00
2025-02-21 18:31:24 +01:00
# Description: This function takes a list of environment variables in YAML format,
# parses them, and exports each key-value pair as environment variables.
# Arguments:
# $1 - A string containing the list of environment variables in YAML format.
# Usage:
# _export_envs "$env_list"
2025-02-21 10:50:11 +01:00
_export_envs( ) {
_env_list = " $1 "
2025-02-21 15:52:02 +01:00
_secure_debug3 "Exporting envs" " $_env_list "
2025-12-20 17:04:04 +01:00
echo " $_env_list " | yq -r 'to_entries | .[] | .key + "=" + .value' | while IFS = '=' read -r _key _value; do
2025-08-31 20:32:52 +02:00
# Using eval to expand nested variables in the configuration file
2025-09-01 01:06:07 +02:00
_value = $( eval 'echo "' " $_value " '"' )
2025-05-27 22:07:22 +02:00
_savedeployconf " $_key " " $_value "
2025-02-21 10:50:11 +01:00
_secure_debug3 " Saved $_key " " $_value "
done
}
2025-02-21 18:31:24 +01:00
# Description:
# This function takes a YAML formatted string of environment variables, parses it,
# and clears each environment variable. It logs the process of clearing each variable.
2025-08-31 20:36:07 +02:00
#
# Note: Environment variables for a hook may be optional and differ between
# services using the same hook.
# If one service sets optional environment variables and another does not, the
# variables may persist and affect subsequent deployments.
# Clearing these variables after each service ensures that only the
# environment variables explicitly specified for each service in the deploy
# file are used.
2025-02-21 18:31:24 +01:00
# Arguments:
# $1 - A YAML formatted string containing environment variable key-value pairs.
# Usage:
# _clear_envs "<yaml_string>"
2025-02-21 10:50:11 +01:00
_clear_envs( ) {
_env_list = " $1 "
2025-02-21 15:52:02 +01:00
_secure_debug3 "Clearing envs" " $_env_list "
2025-12-20 17:04:04 +01:00
env_pairs = $( echo " $_env_list " | yq -r 'to_entries | .[] | .key + "=" + .value' )
2025-02-21 15:52:02 +01:00
2025-02-21 18:23:29 +01:00
echo " $env_pairs " | while IFS = '=' read -r _key _value; do
2025-02-21 10:50:11 +01:00
_debug3 "Deleting key" " $_key "
_cleardomainconf " SAVED_ $_key "
2025-02-21 18:31:24 +01:00
unset -v " $_key "
2025-02-21 10:50:11 +01:00
done
}
2025-02-21 10:50:35 +01:00
2025-02-21 18:44:46 +01:00
# Description:
# This function deploys services listed in the deploy configuration file.
# Arguments:
# $1 - The path to the deploy configuration file.
# $2 - The list of services to deploy.
# Usage:
# _deploy_services "<deploy_file_path>" "<services_list>"
2025-02-21 10:50:35 +01:00
_deploy_services( ) {
_deploy_file = " $1 "
2025-02-21 15:52:02 +01:00
_debug3 "Deploy file" " $_deploy_file "
2025-03-27 19:16:07 +01:00
2025-08-31 14:45:34 +02:00
_tempfile = $( mktemp)
2025-09-06 12:31:56 +02:00
trap 'rm -f $_tempfile' EXIT
2025-02-21 15:52:02 +01:00
2025-12-20 17:04:04 +01:00
yq -r '.services[].name' " $_deploy_file " >" $_tempfile "
2025-08-31 20:41:12 +02:00
_debug3 "Services" " $( cat " $_tempfile " ) "
2025-04-10 10:24:32 +02:00
2025-05-28 20:19:18 +02:00
_failedServices = ""
_failedCount = 0
2025-09-01 01:07:44 +02:00
while read -r _service <& 3; do
2025-02-21 18:23:13 +01:00
_debug2 "Service" " $_service "
2025-12-20 17:04:04 +01:00
_hook = $( yq -r " .services[] | select(.name == \" $_service \").hook " " $_deploy_file " )
_envs = $( yq -r " .services[] | select(.name == \" $_service \").environment " " $_deploy_file " )
2025-02-21 18:23:29 +01:00
2025-02-21 10:50:35 +01:00
_export_envs " $_envs "
2025-05-28 20:02:07 +02:00
if ! _deploy_service " $_service " " $_hook " ; then
2025-05-28 20:19:18 +02:00
_failedServices = " $_service , $_failedServices "
_failedCount = $(( _failedCount + 1 ))
2025-05-28 20:02:07 +02:00
fi
2025-02-21 10:50:35 +01:00
_clear_envs " $_envs "
2025-09-06 12:31:56 +02:00
done 3<" $_tempfile "
2025-05-28 20:02:07 +02:00
2025-05-28 20:19:18 +02:00
_debug3 "Failed services" " $_failedServices "
_debug2 "Failed count" " $_failedCount "
if [ -n " $_failedServices " ] ; then
_info " $( __red "Deployment failed" ) for services: $_failedServices "
2025-05-28 20:02:07 +02:00
else
_debug "All services deployed successfully."
fi
2025-05-28 20:19:18 +02:00
return " $_failedCount "
2025-02-21 10:50:35 +01:00
}
2025-02-21 18:44:46 +01:00
# Description: Deploys a service using the specified hook.
# Arguments:
# $1 - The name of the service to deploy.
# $2 - The hook to use for deployment.
# Usage:
# _deploy_service <service_name> <hook>
2025-02-21 10:50:35 +01:00
_deploy_service( ) {
_name = " $1 "
_hook = " $2 "
_debug2 "SERVICE" " $_name "
_debug2 "HOOK" " $_hook "
_info " $( __green "Deploying" ) to ' $_name ' using ' $_hook ' "
2025-04-13 16:38:28 +02:00
_deploy " $_cdomain " " $_hook "
2025-02-21 10:50:35 +01:00
}