diff --git a/README.md b/README.md index 5477e92..2613110 100644 --- a/README.md +++ b/README.md @@ -25,15 +25,16 @@ postgres:connect Connect via psql to a postgres service postgres:create Create a postgres service postgres:destroy Delete the service and stop its container if there are no links left postgres:export Export a dump of the postgres service database -postgres:expose NOT IMPLEMENTED +postgres:expose [port] Expose a postgres service on custom port if provided (random port otherwise) postgres:import NOT IMPLEMENTED postgres:info Print the connection information postgres:link Link the postgres service to the app postgres:list List all postgres services postgres:logs [-t] Print the most recent log(s) for this service -postgres:restart Graceful shutdown and restart of the service container -postgres:unexpose NOT IMPLEMENTED -postgres:unlink Unlink the postgres service from the app +postgres:restart Graceful shutdown and restart of the postgres service container +postgres:start Start a previously stopped postgres service +postgres:stop Stop a running postgres service +postgres:unexpose Unexpose a previously exposed postgres service ``` ## usage @@ -65,7 +66,7 @@ dokku postgres:link lolipop playground # the above will expose the following environment variables # # DATABASE_URL=postgres://postgres:SOME_PASSWORD@172.17.0.1:5432/lolipop -# DATABASE_NAME=/playground/DATABASE +# DATABASE_NAME=/lolipop/DATABASE # DATABASE_PORT=tcp://172.17.0.1:5432 # DATABASE_PORT_5432_TCP=tcp://172.17.0.1:5432 # DATABASE_PORT_5432_TCP_PROTO=tcp @@ -85,11 +86,10 @@ dokku postgres:logs lolipop dokku postgres:logs lolipop -t # to tail # finally, you can destroy the container -dokku postgres:destroy playground +dokku postgres:destroy lolipop ``` ## todo - implement postgres:clone -- implement postgres:expose - implement postgres:import diff --git a/commands b/commands index b837ec7..c2d70b3 100755 --- a/commands +++ b/commands @@ -39,7 +39,8 @@ case "$1" in touch "$LINKS_FILE" dokku_log_info1 "Starting container" - ID=$(docker run --name "dokku.postgres.$SERVICE" -v "$SERVICE_ROOT:/var/lib/postgresql" -e "POSTGRES_PASSWORD=$password" -d "$PLUGIN_IMAGE:$PLUGIN_IMAGE_VERSION") + SERVICE_NAME=$(get_service_name "$SERVICE") + ID=$(docker run --name "$SERVICE_NAME" -v "$SERVICE_ROOT:/var/lib/postgresql" -e "POSTGRES_PASSWORD=$password" -d --restart always --label dokku=service --label dokku.service=postgres "$PLUGIN_IMAGE:$PLUGIN_IMAGE_VERSION") echo "$ID" > "$SERVICE_ROOT/ID" dokku_log_verbose_quiet "Waiting for container to be ready" @@ -83,9 +84,9 @@ case "$1" in if [[ -f "$SERVICE_ROOT/ID" ]] && docker ps -aq --no-trunc | grep -q $(cat "$SERVICE_ROOT/ID"); then ID=$(cat "$SERVICE_ROOT/ID") - dokku_log_verbose_quiet "Stopping container" - docker stop "$ID" > /dev/null - docker kill "$ID" > /dev/null + service_stop "$SERVICE" + dokku_log_verbose_quiet "Killing container" + docker kill "$ID" > /dev/null || true sleep 1 dokku_log_verbose_quiet "Removing container" @@ -142,14 +143,23 @@ case "$1" in service_logs "$2" "$3" ;; + $PLUGIN_COMMAND_PREFIX:start) + [[ -z $2 ]] && dokku_log_fail "Please specify a name for the service" + verify_service_name "$2" + service_start "$2" + ;; + + $PLUGIN_COMMAND_PREFIX:stop) + [[ -z $2 ]] && dokku_log_fail "Please specify a name for the service" + verify_service_name "$2" + service_stop "$2" + ;; + $PLUGIN_COMMAND_PREFIX:restart) [[ -z $2 ]] && dokku_log_fail "Please specify a name for the service" verify_service_name "$2" - SERVICE="$2"; SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE" - - ID=$(cat "$SERVICE_ROOT/ID") - - docker restart --time=10 "$ID" + service_stop "$2" + service_start "$2" dokku_log_info1 "Please call dokku ps:restart on all linked apps" ;; @@ -185,34 +195,13 @@ case "$1" in $PLUGIN_COMMAND_PREFIX:expose) [[ -z $2 ]] && dokku_log_fail "Please specify a name for the service" verify_service_name "$2" - SERVICE="$2"; SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE"; PORT_FILE="$SERVICE_ROOT/PORT"; DESTINATION_FILE="$SERVICE_ROOT/IPTABLES_DESTINATION" - - [[ -f "$PORT_FILE" ]] && PORT=$(cat "$PORT_FILE") && dokku_log_fail "Service $SERVICE already exposed on port $PORT" - - ID=$(cat "$SERVICE_ROOT/ID") - IP=$(get_container_ip "$ID") - PORT=$(get_random_port) - echo "$PORT" > "$PORT_FILE" - echo "$IP:$PLUGIN_DATASTORE_PORT" > "$DESTINATION_FILE" - - iptables -t nat -A DOCKER -p tcp --dport "$PORT" -j DNAT --to-destination "$IP:$PLUGIN_DATASTORE_PORT" - dokku_log_info1 "Service $SERVICE exposed on port $PORT" + service_port_expose "$2" "${@:3}" ;; $PLUGIN_COMMAND_PREFIX:unexpose) [[ -z $2 ]] && dokku_log_fail "Please specify a name for the service" verify_service_name "$2" - SERVICE="$2"; SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE"; PORT_FILE="$SERVICE_ROOT/PORT"; DESTINATION_FILE="$SERVICE_ROOT/IPTABLES_DESTINATION" - - [[ ! -f "$PORT_FILE" ]] && dokku_log_fail "Service not exposed" - - ID=$(cat "$SERVICE_ROOT/ID") - IP=$(get_container_ip "$ID") - PORT=$(cat "$PORT_FILE") - DESTINATION=$(cat "$DESTINATION_FILE") - - iptables -t nat -D DOCKER -p tcp --dport "$PORT" -j DNAT --to-destination "$DESTINATION" - rm -rf "$PORT_FILE" + service_port_unexpose "$2" ;; help) @@ -226,12 +215,14 @@ case "$1" in $PLUGIN_COMMAND_PREFIX:import , NOT IMPLEMENTED $PLUGIN_COMMAND_PREFIX:connect , Connect via psql to a $PLUGIN_SERVICE service $PLUGIN_COMMAND_PREFIX:logs [-t], Print the most recent log(s) for this service - $PLUGIN_COMMAND_PREFIX:restart , Graceful shutdown and restart of the service container + $PLUGIN_COMMAND_PREFIX:restart , Graceful shutdown and restart of the $PLUGIN_SERVICE service container $PLUGIN_COMMAND_PREFIX:info , Print the connection information $PLUGIN_COMMAND_PREFIX:list, List all $PLUGIN_SERVICE services $PLUGIN_COMMAND_PREFIX:clone , NOT IMPLEMENTED - $PLUGIN_COMMAND_PREFIX:expose , NOT IMPLEMENTED - $PLUGIN_COMMAND_PREFIX:unexpose , NOT IMPLEMENTED + $PLUGIN_COMMAND_PREFIX:expose [port], Expose a $PLUGIN_SERVICE service on custom port if provided (random port otherwise) + $PLUGIN_COMMAND_PREFIX:unexpose , Unexpose a previously exposed $PLUGIN_SERVICE service + $PLUGIN_COMMAND_PREFIX:start , Start a previously stopped $PLUGIN_SERVICE service + $PLUGIN_COMMAND_PREFIX:stop , Stop a running $PLUGIN_SERVICE service EOF ;; diff --git a/config b/config index 41cd383..4d0a28a 100644 --- a/config +++ b/config @@ -5,7 +5,7 @@ export POSTGRES_ROOT=/var/lib/dokku/services/postgres export PLUGIN_COMMAND_PREFIX="postgres" export PLUGIN_DATA_ROOT=$POSTGRES_ROOT -export PLUGIN_DATASTORE_PORT=5432 +export PLUGIN_DATASTORE_PORTS=(5432) export PLUGIN_DEFAULT_ALIAS="DATABASE" export PLUGIN_IMAGE=$POSTGRES_IMAGE export PLUGIN_IMAGE_VERSION=$POSTGRES_IMAGE_VERSION diff --git a/docker-args b/docker-args index 9e39800..b36214b 100755 --- a/docker-args +++ b/docker-args @@ -1,6 +1,6 @@ #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x -source "$(dirname "$0")/../common/functions" +source "$PLUGIN_PATH/common/functions" source "$(dirname "$0")/functions" source "$(dirname "$0")/config" diff --git a/functions b/functions index 42416d4..d32a735 100755 --- a/functions +++ b/functions @@ -2,19 +2,21 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$(dirname "$0")/config" -get_random_port() { - local port=$RANDOM - local quit=0 - - while [ "$quit" -ne 1 ]; do - netstat -a | grep $port >> /dev/null - if [ $? -gt 0 ]; then - quit=1 - else - port=$((port + 1)) - fi +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 - echo $port } get_container_ip() { @@ -54,11 +56,19 @@ service_list() { else dokku_log_info1_quiet "$PLUGIN_SERVICE services:" for SERVICE in $SERVICES; do - dokku_log_verbose "$SERVICE $(service_status $SERVICE)" + dokku_log_verbose "$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 + printf ", exposed port(s): %s" "$(cat "$PORT_FILE")" +} + service_link() { local APP="$2" local SERVICE="$1" @@ -111,6 +121,109 @@ service_status() { echo "(stopped)" && return 0 } +service_port_expose() { + service_start "$1" + 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:postgres" --name "$EXPOSED_NAME" $(docker_ports_options "${PORTS[@]}") --restart always --label dokku=ambassador --label dokku.ambassador=postgres 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 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 + [[ -n $ID ]] && dokku_log_warn "Service is already started" && return 0 + + 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" @@ -133,7 +246,7 @@ service_url() { local ID="$(cat "$SERVICE_ROOT/ID")" local IP="$(get_container_ip "$ID")" local PASSWORD="$(cat "$SERVICE_ROOT/PASSWORD")" - echo "$PLUGIN_SCHEME://postgres:$PASSWORD@$IP:$PLUGIN_DATASTORE_PORT/$SERVICE" + echo "$PLUGIN_SCHEME://postgres:$PASSWORD@$IP:${PLUGIN_DATASTORE_PORTS[0]}/$SERVICE" } is_container_status () { @@ -147,3 +260,8 @@ is_container_status () { return 1 fi } + +get_service_name() { + local SERVICE="$1" + echo "dokku.${PLUGIN_COMMAND_PREFIX}.$SERVICE" +} diff --git a/install b/install index 91a71d1..f3b2bed 100755 --- a/install +++ b/install @@ -6,5 +6,7 @@ if ! docker images | grep -e "^$PLUGIN_IMAGE " | grep -q $PLUGIN_IMAGE_VERSION ; docker pull $PLUGIN_IMAGE:$PLUGIN_IMAGE_VERSION fi +docker pull svendowideit/ambassador:latest + mkdir -p $PLUGIN_DATA_ROOT || echo "Failed to create $PLUGIN_SERVICE directory" chown dokku:dokku $PLUGIN_DATA_ROOT