Skip to content

Commit

Permalink
Move to new dokku 0.5 coding style
Browse files Browse the repository at this point in the history
closes #31 #33 and breaks backwards compatibility with dokku 0.4

* Refactor the plugin to use the subcommands structure and coding
conventions of dokku 0.5

* Use sigil for templating

* A lot of general code cleanups and improvements
  • Loading branch information
sseemayer committed Apr 26, 2016
1 parent ece6eb6 commit 58d7cfb
Show file tree
Hide file tree
Showing 9 changed files with 451 additions and 374 deletions.
146 changes: 20 additions & 126 deletions commands
Original file line number Diff line number Diff line change
@@ -1,146 +1,40 @@
#!/usr/bin/env bash
[[ " help letsencrypt:help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/domains/functions"

PLUGIN_BASE_PATH="$PLUGIN_PATH"
if [[ -n $DOKKU_API_VERSION ]]; then
PLUGIN_BASE_PATH="$PLUGIN_ENABLED_PATH"
fi

source "$PLUGIN_BASE_PATH/letsencrypt/functions"
source "$PLUGIN_BASE_PATH/nginx-vhosts/functions"
source "$PLUGIN_BASE_PATH/certs/functions"
source "$PLUGIN_BASE_PATH/common/functions"

if [[ $1 == letsencrypt || $1 == letsencrypt:* ]]; then

if [[ ! -z $2 ]]; then
verify_app_name "$2"
APP="$2"
APP_ROOT="$DOKKU_ROOT/$APP"
APP_SSL_ROOT="$DOKKU_ROOT/$APP/tls"
LETSENCRYPT_ROOT="$APP_ROOT/letsencrypt"
fi

# by default, renew 30 days before expiry
LETSENCRYPT_GRACEPERIOD_DEFAULT=$((30 * 24 * 60 * 60));
fi

case "$1" in
letsencrypt)
[[ -z $APP ]] && echo "Please specify an app to run the command on" && exit 1

dokku_log_info2 "Let's Encrypt $APP..."

# dynamically choose a port for the letsencrypt standalone authenticator
ACMEPORT=$(get_available_port)

letsencrypt_check_email

letsencrypt_update
letsencrypt_acmeproxy_on
letsencrypt_acme || true # remove ACME proxy even if this fails
letsencrypt_acmeproxy_off

dokku_log_verbose "done"

;;

letsencrypt:revoke)
[[ -z $APP ]] && echo "Please specify an app to run the command on" && exit 1

dokku_log_info2 "Revoke Let's Encrypt certificate from $APP..."

letsencrypt_check_email
letsencrypt_update
letsencrypt_acme_revoke || true

dokku_log_verbose "done"
;;

letsencrypt:cleanup)
[[ -z $APP ]] && echo "Please specify an app to run the command on" && exit 1
letsencrypt_cleanup

;;

letsencrypt:ls)
help | letsencrypt:help)

dokku_col_log_info1_quiet "App name" "Certificate Expiry" "Time before expiry" "Time before renewal"
help_content_func() {
declare desc="return letsencrypt plugin help content"

letsencrypt_list_apps_and_expiry |
sort -nk2 |
while IFS=$'\t' read -r -a appExpiry; do
expire_date=$(date -d @${appExpiry[1]} +"%F %T")
expire_time=$(letsencrypt_format_timediff ${appExpiry[3]});
renew_time=$(letsencrypt_format_timediff ${appExpiry[4]});
dokku_col_log_msg "${appExpiry[0]}" "${expire_date}" "${expire_time}" "${renew_time}"

done

;;

letsencrypt:auto-renew)

if [ -z "$APP" ]; then
dokku_log_info2 "Auto-renewing all apps..."

# For all apps, sorted by ascending time left until renewal.
# This way, we'll prioritize apps that need to be renewed soon
# if we should hit a rate limit along the way.
letsencrypt_list_apps_and_expiry |
sort -nk5 |
while IFS=$'\t' read -r -a appExpiry; do

if [[ ${appExpiry[4]} -lt 0 ]]; then
dokku_log_info1 "${appExpiry[0]} needs renewal"
dokku letsencrypt ${appExpiry[0]}
else
days_left=$(letsencrypt_format_timediff ${appExpiry[4]})
dokku_log_verbose "${appExpiry[0]} still has $days_left days left before renewal"
fi

done

dokku_log_info2 "Finished auto-renewal"

else
expiry=$(letsencrypt_get_expirydate $APP)
grace_period=$(letsencrypt_get $APP DOKKU_LETSENCRYPT_GRACEPERIOD $LETSENCRYPT_GRACEPERIOD_DEFAULT);
time_to_expiry=$(( $expiry - $(date +%s) ))
time_to_renewal=$(( $expiry - $grace_period - $(date +%s) ))

if [[ $time_to_renewal -lt 0 ]]; then
dokku_log_info2 "Auto-renew $APP..."
dokku letsencrypt $APP
else
days_left=$(letsencrypt_format_timediff $time_to_renewal)
dokku_log_verbose "$APP still has $days_left days left before renewal"
fi

fi

;;

help)
HELP=$(cat<<EOF
cat<<help_content
letsencrypt <app>, Enable or renew letsencrypt for app
letsencrypt:auto-renew, Auto-renew all apps secured by letsencrypt if renewal is necessary
letsencrypt:auto-renew <app>, Auto-renew app if renewal is necessary
letsencrypt:help, Display letsencrypt help
letsencrypt:cleanup <app>, Remove stale certificate directories for app
letsencrypt:revoke <app>, Revoke letsencrypt certificate for app
letsencrypt:ls, List letsencrypt-secured apps with certificate expiry times
EOF
)
if [[ -n $DOKKU_API_VERSION ]]; then
echo "$HELP"
help_content
}

if [[ $1 = "letsencrypt:help" ]]; then
echo -e 'Usage: dokku letsencrypt[:COMMAND]'
echo ''
echo 'Automatically retrieve and install Lets Encrypt certificates.'
echo ''
echo 'Additional commands:'
help_content_func | sort | column -c2 -t -s,
else
cat && echo "$HELP"
help_content_func
fi
;;

;;

*)
exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
;;
;;

esac
Loading

2 comments on commit 58d7cfb

@MorrisJobke
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as a sidenote: That's a huge commit ...not that easy to follow as possible contributor ;)

@sseemayer
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about the giant commit - I unfortunately had to touch pretty much everything at the same time in this refactoring.

The easiest way to understand what happened is probably to check out the code before and after this commit and diff manually (or possibly use a two-column diff viewer such as meld) using this handy guide:

  • moved all of the logic from commands into appropriate files in subcommands.
  • moved functions from functions that are only used by one subcommand into that subcommand.
  • added declare desc= statements to all functions
  • removed reliance on globally defined variables such as $APP, $APP_ROOT, etc. and pass pertinent data as arguments to the functions that need them
  • made variables local and gave them lower-case friendlier-to-type names
  • removed letsencrypt_get mechanism for getting config variables in favor of the config_export and config_get functions of the core config plugin.
  • switched to sigil for templating

I'm also happy to answer questions. Hope this helps anyone interested!

Please sign in to comment.