-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Multideploy: Deploy to multiple hooks of the same type #6241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 28 commits
cd9c764
0ce2830
cfe3226
f1d214a
18575b1
a6060f9
4fbade3
f275f3c
06e75de
6189aa5
ccd9d9a
8d77bef
a9c2435
7a35d68
b6a6e67
98aaff6
f26d404
488d147
69858fb
e9b5bbf
67b5176
2e6065c
bc23e8c
7949c10
c6c672b
5189a31
ccf11cc
d3a8c7f
eca6e69
1dc597b
3551e4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ RUN apk --no-cache add -f \ | |
tar \ | ||
libidn \ | ||
jq \ | ||
yq \ | ||
cronie | ||
|
||
ENV LE_CONFIG_HOME=/acme.sh | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
#!/usr/bin/env sh | ||
|
||
################################################################################ | ||
# ACME.sh 3rd party deploy plugin for multiple (same) services | ||
################################################################################ | ||
# Authors: tomo2403 (creator), https://github.com/tomo2403 | ||
# Updated: 2025-03-01 | ||
# Issues: https://github.com/acmesh-official/acme.sh/issues and mention @tomo2403 | ||
################################################################################ | ||
# Usage (shown values are the examples): | ||
# 1. Set optional environment variables | ||
# - export MULTIDEPLOY_FILENAME="multideploy.yaml" - "multideploy.yml" will be automatically used if not set" | ||
# | ||
# 2. Run command: | ||
# acme.sh --deploy --deploy-hook multideploy -d example.com | ||
################################################################################ | ||
# Dependencies: | ||
# - yq | ||
################################################################################ | ||
# Return value: | ||
# 0 means success, otherwise error. | ||
################################################################################ | ||
|
||
MULTIDEPLOY_VERSION="1.0" | ||
|
||
# Description: This function handles the deployment of certificates to multiple services. | ||
# It processes the provided certificate files and deploys them according to the | ||
# configuration specified in the multideploy file. | ||
# | ||
# 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. | ||
multideploy_deploy() { | ||
_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" | ||
|
||
DOMAIN_DIR=$_cdomain | ||
if echo "$DOMAIN_PATH" | grep -q "$ECC_SUFFIX"; then | ||
DOMAIN_DIR="$DOMAIN_DIR"_ecc | ||
fi | ||
_debug2 "DOMAIN_DIR" "$DOMAIN_DIR" | ||
|
||
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'." | ||
else | ||
_savedeployconf "MULTIDEPLOY_FILENAME" "$MULTIDEPLOY_FILENAME" | ||
_debug2 "MULTIDEPLOY_FILENAME" "$MULTIDEPLOY_FILENAME" | ||
fi | ||
|
||
OLDIFS=$IFS | ||
if ! file=$(_preprocess_deployfile "$MULTIDEPLOY_FILENAME"); then | ||
_err "Failed to preprocess deploy file." | ||
return 1 | ||
fi | ||
_debug3 "File" "$file" | ||
|
||
# Deploy to services | ||
_deploy_services "$file" | ||
|
||
# Save deployhook for renewals | ||
_debug2 "Setting Le_DeployHook" | ||
_savedomainconf "Le_DeployHook" "multideploy" | ||
tomo2403 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return 0 | ||
} | ||
|
||
# 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: | ||
# _preprocess_deployfile "<deploy_file1>" "<deploy_file2>?" | ||
_preprocess_deployfile() { | ||
# 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 | ||
_debug3 "yq is installed." | ||
|
||
# Check if deploy file exists | ||
IFS=$(printf '\n') | ||
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 | ||
IFS=$OLDIFS | ||
|
||
if [ -n "$found_file" ]; then | ||
_check_deployfile "$DOMAIN_PATH/$found_file" | ||
else | ||
_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." | ||
return 1 | ||
fi | ||
|
||
echo "$DOMAIN_PATH/$found_file" | ||
} | ||
|
||
# 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: | ||
# _check_deployfile "<deploy_file_path>" | ||
_check_deployfile() { | ||
_deploy_file="$1" | ||
_debug2 "Deploy file" "$_deploy_file" | ||
|
||
# Check version | ||
_deploy_file_version=$(yq '.version' "$_deploy_file") | ||
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." | ||
return 1 | ||
fi | ||
_debug2 "Deploy file version is compatible: $_deploy_file_version" | ||
|
||
# Extract all services from config | ||
_services=$(yq e '.services[].name' "$_deploy_file") | ||
_debug2 "Services" "$_services" | ||
|
||
if [ -z "$_services" ]; then | ||
_err "Config does not have any services to deploy to." | ||
return 1 | ||
fi | ||
_debug2 "Config has services." | ||
|
||
IFS=$(printf '\n') | ||
# Check if extracted services exist in services list | ||
for _service in $_services; do | ||
_debug2 "Checking service" "$_service" | ||
# Check if service exists | ||
if ! yq e ".services[] | select(.name == \"$_service\")" "$_deploy_file" >/dev/null; then | ||
_err "Service '$_service' not found." | ||
return 1 | ||
fi | ||
|
||
# Check if service has hook | ||
if ! yq e ".services[] | select(.name == \"$_service\").hook" "$_deploy_file" >/dev/null; then | ||
_err "Service '$_service' does not have a hook." | ||
return 1 | ||
fi | ||
|
||
# Check if service has environment | ||
if ! yq e ".services[] | select(.name == \"$_service\").environment" "$_deploy_file" >/dev/null; then | ||
_err "Service '$_service' does not have an environment." | ||
return 1 | ||
fi | ||
done | ||
IFS=$OLDIFS | ||
} | ||
|
||
# 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" | ||
_export_envs() { | ||
_env_list="$1" | ||
|
||
_secure_debug3 "Exporting envs" "$_env_list" | ||
|
||
IFS=$(printf '\n') | ||
echo "$_env_list" | yq e -r 'to_entries | .[] | .key + "=" + .value' | while IFS='=' read -r _key _value; do | ||
_value=$(eval echo "$_value") | ||
tomo2403 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
_savedomainconf "$_key" "$_value" | ||
tomo2403 marked this conversation as resolved.
Show resolved
Hide resolved
tomo2403 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
_secure_debug3 "Saved $_key" "$_value" | ||
done | ||
IFS=$OLDIFS | ||
} | ||
|
||
# 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. | ||
# Arguments: | ||
# $1 - A YAML formatted string containing environment variable key-value pairs. | ||
# Usage: | ||
# _clear_envs "<yaml_string>" | ||
_clear_envs() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to clear There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If a hook has optional envs and you have two services that use the same hook, but one service uses the optional envs and the other doesn't, both services will use the optional envs because they are saved by the first service. Clearing them ensures that multideploy only uses the envs specified in the deploy file |
||
_env_list="$1" | ||
|
||
_secure_debug3 "Clearing envs" "$_env_list" | ||
env_pairs=$(echo "$_env_list" | yq e -r 'to_entries | .[] | .key + "=" + .value') | ||
|
||
IFS=$(printf '\n') | ||
echo "$env_pairs" | while IFS='=' read -r _key _value; do | ||
_debug3 "Deleting key" "$_key" | ||
_cleardomainconf "SAVED_$_key" | ||
unset -v "$_key" | ||
done | ||
IFS="$OLDIFS" | ||
} | ||
|
||
# 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>" | ||
_deploy_services() { | ||
_deploy_file="$1" | ||
_debug3 "Deploy file" "$_deploy_file" | ||
|
||
_services=$(yq e '.services[].name' "$_deploy_file") | ||
_debug3 "Services" "$_services" | ||
|
||
_service_list=$(printf '%s\n' "$_services") | ||
|
||
for _service in $(printf '%s\n' "$_service_list"); do | ||
_debug2 "Service" "$_service" | ||
_hook=$(yq e ".services[] | select(.name == \"$_service\").hook" "$_deploy_file") | ||
_envs=$(yq e ".services[] | select(.name == \"$_service\").environment[]" "$_deploy_file") | ||
|
||
_export_envs "$_envs" | ||
_deploy_service "$_service" "$_hook" | ||
_clear_envs "$_envs" | ||
done | ||
} | ||
|
||
# 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> | ||
_deploy_service() { | ||
_name="$1" | ||
_hook="$2" | ||
|
||
_debug2 "SERVICE" "$_name" | ||
_debug2 "HOOK" "$_hook" | ||
|
||
_info "$(__green "Deploying") to '$_name' using '$_hook'" | ||
_deploy "$_cdomain" "$_hook" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do you need this DOMAIN_DIR ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be used in the deploy file to specify the path for a cert if you are going to have multiple certs on the same service. This makes it easier to copy the deploy file to another domain dir.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which deploy file uses it ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
have a look at the sample config above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which one ?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
first comment after pr opening. Both services use DOMAIN_DIR. I use it for all my domains because they are deployed to the same service. If I change something in one deploy file I can just copy the file to all other domains without having to change the path for each domain in my docker container