Files
dokku-redis/functions
Loïc Guitaut baeea96731 Revamp link/unlink commands
Previously we were exporting `REDIS_URL` via the docker-args* hooks.
This seems to confuse our users (since the env var is not displayed
when calling `dokku config`) and in some cases it also seems that the
env var is not correctly set.
Another problem is if several services are linked to the same app and
if they are exporting `REDIS_URL` as well. Then we don’t know what
will be set.

To resolve theses issues, this patch changes the way we manage the env
vars. We use standard dokku commands (`config` and `docker-options`) so
config is set on the linked application and can be reviewed by the user
easily.
We also handle the case where `REDIS_URL` is already set on the
linked application. When it’s the case, we automatically generate
another env var based on the following pattern: DOKKU_<service
name>_<random unused color>_URL. For example, this can give:
DOKKU_REDIS_BLACK_URL.

Since naming is now handled automatically, the `alias` command has been
removed. If the user wants to set a different env var on her app, it’s
just a matter of using `dokku config:set` and pasting the wanted value.

IP in DSN has been removed in favor of host name exported by docker in
the container. This is more robust and simpler since the IP can change
but the name will remain the same if the service container restarts for
instance.

With all those changes, a new command has been introduced: `promote`.
The goal of this command is to easily set a service as the primary one
when several are linked to an app. (see README for an example)
2015-10-05 15:29:36 +02:00

345 lines
10 KiB
Bash
Executable File

#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$(dirname "$0")/config"
get_random_ports() {
local iterations="${1:-1}"
for (( i=0; i < iterations; i++ )); do
local port=$RANDOM
local quit=0
while [ "$quit" -ne 1 ]; do
netstat -an | grep $port > /dev/null
if [ $? -gt 0 ]; then
quit=1
else
port=$((port + 1))
fi
done
echo $port
done
}
get_container_ip() {
docker inspect --format '{{ .NetworkSettings.IPAddress }}' "$1"
}
verify_service_name() {
local SERVICE="$1"
[[ ! -n "$SERVICE" ]] && dokku_log_fail "(verify_service_name) SERVICE must not be null"
[[ ! -d "$PLUGIN_DATA_ROOT/$SERVICE" ]] && dokku_log_fail "$PLUGIN_SERVICE service $SERVICE does not exist"
return 0
}
service_alias() {
local SERVICE="$1"
local SERVICE_NAME=$(get_service_name "$SERVICE")
echo "$SERVICE_NAME" | tr ._ -
}
service_info() {
local SERVICE="$1"
local SERVICE_URL=$(service_url "$SERVICE")
echo " DSN: $SERVICE_URL"
}
service_list() {
local SERVICES=$(ls "$PLUGIN_DATA_ROOT" 2> /dev/null)
if [[ -z $SERVICES ]]; then
dokku_log_warn "There are no $PLUGIN_SERVICE services"
else
dokku_log_info1_quiet "$PLUGIN_SERVICE services:"
for SERVICE in $SERVICES; do
dokku_log_verbose "$SERVICE, $(service_version "$SERVICE") $(service_status "$SERVICE")$(service_exposed_ports "$SERVICE")"
done
fi
}
service_exposed_ports() {
local SERVICE="$1"
local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE"
local PORT_FILE="$SERVICE_ROOT/PORT"
[[ ! -f $PORT_FILE ]] && return 0
local PORTS=($(cat "$PORT_FILE"))
echo -n ", exposed port(s):"
for (( i=0; i < ${#PLUGIN_DATASTORE_PORTS[@]}; i++ )); do
echo -n " ${PLUGIN_DATASTORE_PORTS[i]}->${PORTS[i]}"
done
echo ""
}
service_link() {
local APP="$2"
local SERVICE="$1"
local SERVICE_URL=$(service_url "$SERVICE")
local SERVICE_NAME=$(get_service_name "$SERVICE")
local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE"
local EXISTING_CONFIG=$(dokku config "$APP")
local LINK=$(echo "$EXISTING_CONFIG" | grep "$SERVICE_URL" | cut -d: -f1) || true
local DEFAULT_ALIAS=$(echo "$EXISTING_CONFIG" | grep "${PLUGIN_DEFAULT_ALIAS}_URL") || true
local SERVICE_ALIAS=$(service_alias "$SERVICE")
local LINKS_FILE="$SERVICE_ROOT/LINKS"
[[ -n $LINK ]] && dokku_log_fail "Already linked as $LINK"
mkdir -p "$SERVICE_ROOT" || dokku_log_fail "Unable to create service directory"
touch "$LINKS_FILE"
echo "$APP" >> "$LINKS_FILE"
sort "$LINKS_FILE" -u -o "$LINKS_FILE"
local ALIAS="$PLUGIN_DEFAULT_ALIAS"
if [[ -n $DEFAULT_ALIAS ]]; then
ALIAS=$(service_alternative_alias "$EXISTING_CONFIG")
fi
dokku docker-options:add "$APP" build,deploy,run "--link $SERVICE_NAME:$SERVICE_ALIAS"
dokku config:set "$APP" "${ALIAS}_URL=$SERVICE_URL"
}
service_alternative_alias() {
local EXISTING_CONFIG="$1"
local COLORS=(AQUA BLACK BLUE FUCHSIA GRAY GREEN LIME MAROON NAVY OLIVE PURPLE RED SILVER TEAL WHITE YELLOW)
local ALIAS;
while [[ -z $ALIAS ]]; do
local IDX=$((RANDOM % ${#COLORS[*]}))
local COLOR=${COLORS[IDX]}
ALIAS="${PLUGIN_ALT_ALIAS}_${COLOR}"
local IN_USE=$(echo "$EXISTING_CONFIG" | grep "${ALIAS}_URL")
if [[ -n $IN_USE ]]; then
unset ALIAS
fi
done
echo "$ALIAS"
}
service_logs() {
local SERVICE="$1"
local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE"
local ID=$(cat "$SERVICE_ROOT/ID")
if [[ $2 == "-t" ]]; then
DOKKU_LOGS_ARGS="--follow"
else
DOKKU_LOGS_ARGS="--tail 100"
fi
docker logs $DOKKU_LOGS_ARGS "$ID"
}
service_set_alias() {
local SERVICE="$1"
local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE"
local ALIAS_FILE="$SERVICE_ROOT/ALIAS"
mkdir -p "$SERVICE_ROOT" || dokku_log_fail "Unable to create service directory"
touch "$ALIAS_FILE"
echo "$2" > "$ALIAS_FILE"
}
service_status() {
local SERVICE="$1"
local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE"
local ID="$(cat "$SERVICE_ROOT/ID")"
is_container_status "$ID" "Dead" && echo "(dead)" && return 0
is_container_status "$ID" "OOMKilled" && echo "(oomkilled)" && return 0
is_container_status "$ID" "Paused" && echo "(paused)" && return 0
is_container_status "$ID" "Restarting" && echo "(restarting)" && return 0
is_container_status "$ID" "Running" && echo "(running)" && return 0
echo "(stopped)" && return 0
}
service_port_expose() {
service_start "$1" "true"
service_port_unpause "$1" "true" "${@:2}"
}
service_port_pause() {
local SERVICE="$1"
local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE"
local EXPOSED_NAME="$(get_service_name "$SERVICE").ambassador"
local PORT_FILE="$SERVICE_ROOT/PORT"
local LOG_FAIL="$2"
if [[ "$LOG_FAIL" == "true" ]]; then
[[ ! -f "$PORT_FILE" ]] && dokku_log_fail "Service not exposed"
else
[[ ! -f "$PORT_FILE" ]] && return 0
fi
docker stop "$EXPOSED_NAME" > /dev/null
docker rm "$EXPOSED_NAME" > /dev/null
if [[ "$LOG_FAIL" == "true" ]]; then
dokku_log_info1 "Service $SERVICE unexposed"
fi
}
service_port_unexpose() {
local SERVICE="$1"
local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE"
local PORT_FILE="$SERVICE_ROOT/PORT"
service_port_pause "$SERVICE" "true"
rm -rf "$PORT_FILE"
}
service_port_unpause() {
local SERVICE="$1"
local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE"
local SERVICE_NAME=$(get_service_name "$SERVICE")
local EXPOSED_NAME="${SERVICE_NAME}.ambassador"
local PORT_FILE="$SERVICE_ROOT/PORT"
local LOG_FAIL="$2"
local PORTS=(${@:3})
PORTS=(${PORTS[@]:-$(get_random_ports ${#PLUGIN_DATASTORE_PORTS[@]})})
local ID=$(cat "$SERVICE_ROOT/ID")
[[ "${#PORTS[@]}" != "${#PLUGIN_DATASTORE_PORTS[@]}" ]] && dokku_log_fail "${#PLUGIN_DATASTORE_PORTS[@]} ports to be exposed need to be provided"
if [[ "$LOG_FAIL" == "true" ]]; then
[[ -f "$PORT_FILE" ]] && PORTS=($(cat "$PORT_FILE")) && dokku_log_fail "Service $SERVICE already exposed on port(s) ${PORTS[*]}"
else
[[ ! -f "$PORT_FILE" ]] && return 0
PORTS=($(cat "$PORT_FILE"))
fi
echo "${PORTS[@]}" > "$PORT_FILE"
docker run -d --link "$SERVICE_NAME:redis" --name "$EXPOSED_NAME" $(docker_ports_options "${PORTS[@]}") --restart always --label dokku=ambassador --label dokku.ambassador=redis svendowideit/ambassador > /dev/null
if [[ "$LOG_FAIL" == "true" ]]; then
dokku_log_info1 "Service $SERVICE exposed on port(s) ${PORTS[*]}"
fi
}
docker_ports_options() {
local PORTS=("$@")
for (( i=0; i < ${#PLUGIN_DATASTORE_PORTS[@]}; i++ )); do
echo -n "-p ${PORTS[i]}:${PLUGIN_DATASTORE_PORTS[i]} "
done
}
service_start() {
local SERVICE="$1"
local QUIET="$2"
local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE"
local SERVICE_NAME=$(get_service_name "$SERVICE")
local ID=$(docker ps -f status=running | grep "$SERVICE_NAME" | awk '{print $1}') || true
if [[ -n $ID ]]; then
[[ -z $QUIET ]] && dokku_log_warn "Service is already started"
return 0
fi
dokku_log_info1_quiet "Starting container"
local PREVIOUS_ID=$(docker ps -f status=exited | grep "$SERVICE_NAME" | awk '{print $1}') || true
if [[ -n $PREVIOUS_ID ]]; then
docker start "$PREVIOUS_ID" > /dev/null
service_port_unpause "$SERVICE"
dokku_log_info2 "Container started"
else
dokku_log_verbose_quiet "No container exists for $SERVICE"
fi
}
service_stop() {
local SERVICE="$1"
local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE";
local SERVICE_NAME=$(get_service_name "$SERVICE")
local ID=$(docker ps -f status=running | grep "$SERVICE_NAME" | awk '{print $1}') || true
[[ -z $ID ]] && dokku_log_warn "Service is already stopped" && return 0
if [[ -n $ID ]]; then
dokku_log_info1_quiet "Stopping container"
docker stop "$SERVICE_NAME" > /dev/null
service_port_pause "$SERVICE"
dokku_log_info2 "Container stopped"
else
dokku_log_verbose_quiet "No container exists for $SERVICE"
fi
}
service_unlink() {
local APP="$2"
local SERVICE="$1"
local SERVICE_URL=$(service_url "$SERVICE")
local SERVICE_NAME=$(get_service_name "$SERVICE")
local EXISTING_CONFIG=$(dokku config "$APP")
local SERVICE_ALIAS=$(service_alias "$SERVICE")
local LINK=($(echo "$EXISTING_CONFIG" | grep "$SERVICE_URL" | cut -d: -f1)) || true
[[ -z ${LINK[*]} ]] && dokku_log_fail "Not linked to app $APP"
remove_from_links_file "$SERVICE" "$APP"
dokku docker-options:remove "$APP" build,deploy,run "--link $SERVICE_NAME:$SERVICE_ALIAS"
dokku config:unset "$APP" "${LINK[*]}"
}
service_url() {
local SERVICE="$1"
local SERVICE_ALIAS="$(service_alias "$SERVICE")"
echo "$PLUGIN_SCHEME://$SERVICE_ALIAS:${PLUGIN_DATASTORE_PORTS[0]}/0"
}
is_container_status () {
local CID=$1
local TEMPLATE="{{.State.$2}}"
local CONTAINER_STATUS=$(docker inspect -f "$TEMPLATE" "$CID" || true)
if [[ "$CONTAINER_STATUS" == "true" ]]; then
return 0
else
return 1
fi
}
get_service_name() {
local SERVICE="$1"
echo "dokku.${PLUGIN_COMMAND_PREFIX}.$SERVICE"
}
service_version() {
local SERVICE="$1"
local SERVICE_NAME="$(get_service_name "$SERVICE")"
docker inspect -f '{{.Config.Image}}' "$SERVICE_NAME"
}
get_url_from_config() {
local EXISTING_CONFIG="$1"
local CONFIG_VAR="$2"
echo "$EXISTING_CONFIG" | grep "$CONFIG_VAR" | sed "s/$CONFIG_VAR:\s*//"
}
promote() {
local SERVICE="$1"
local APP="$2"
local PLUGIN_DEFAULT_CONFIG_VAR="${PLUGIN_DEFAULT_ALIAS}_URL"
local EXISTING_CONFIG=$(dokku config "$APP")
local SERVICE_URL=$(service_url "$SERVICE")
local CONFIG_VARS=($(echo "$EXISTING_CONFIG" | grep "$SERVICE_URL" | cut -d: -f1)) || true
local PREVIOUS_DEFAULT_URL=$(get_url_from_config "$EXISTING_CONFIG" "$PLUGIN_DEFAULT_CONFIG_VAR")
[[ -z ${CONFIG_VARS[*]} ]] && dokku_log_fail "Not linked to app $APP"
[[ ${CONFIG_VARS[*]} =~ $PLUGIN_DEFAULT_CONFIG_VAR ]] && dokku_log_fail "Service $1 already promoted as $PLUGIN_DEFAULT_CONFIG_VAR"
local NEW_CONFIG_VARS=""
if [[ -n $PREVIOUS_DEFAULT_URL ]]; then
local PREVIOUS_ALIAS=$(echo "$EXISTING_CONFIG" | grep "$PREVIOUS_DEFAULT_URL" | grep -v "$PLUGIN_DEFAULT_CONFIG_VAR") || true
if [[ -z $PREVIOUS_ALIAS ]]; then
local ALIAS=$(service_alternative_alias "$EXISTING_CONFIG")
NEW_CONFIG_VARS+="${ALIAS}_URL=$PREVIOUS_DEFAULT_URL "
fi
fi
local PROMOTE_URL=$(get_url_from_config "$EXISTING_CONFIG" "${CONFIG_VARS[0]}")
NEW_CONFIG_VARS+="$PLUGIN_DEFAULT_CONFIG_VAR=$PROMOTE_URL"
dokku config:set "$APP" $NEW_CONFIG_VARS
}
remove_from_links_file() {
local SERVICE="$1"
local APP="$2"
local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE"
local LINKS_FILE="$SERVICE_ROOT/LINKS"
mkdir -p "$SERVICE_ROOT" || dokku_log_fail "Unable to create service directory"
touch "$LINKS_FILE"
sed -i "/^$APP\$/d" "$LINKS_FILE"
sort "$LINKS_FILE" -u -o "$LINKS_FILE"
}