From ec9b8be5f8676c2662937d3b69241dab7a38dd0b Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 21 Apr 2017 01:51:32 -0600 Subject: [PATCH] feat: implement first-pass at networking plugin Implements help, `rebuild`, and `rebuildall` command. Refs #2403 --- docs/networking/network.md | 32 ++++++ plugins/network/commands | 15 +++ plugins/network/functions | 132 +++++++++++++++++++++++++ plugins/network/internal-functions | 27 +++++ plugins/network/plugin.toml | 4 + plugins/network/subcommands/default | 5 + plugins/network/subcommands/rebuild | 13 +++ plugins/network/subcommands/rebuildall | 14 +++ 8 files changed, 242 insertions(+) create mode 100644 docs/networking/network.md create mode 100755 plugins/network/commands create mode 100755 plugins/network/functions create mode 100755 plugins/network/internal-functions create mode 100644 plugins/network/plugin.toml create mode 100755 plugins/network/subcommands/default create mode 100755 plugins/network/subcommands/rebuild create mode 100755 plugins/network/subcommands/rebuildall diff --git a/docs/networking/network.md b/docs/networking/network.md new file mode 100644 index 000000000..aa737dc15 --- /dev/null +++ b/docs/networking/network.md @@ -0,0 +1,32 @@ +# Network Management + +> New as of 0.10.0 + +``` +network:rebuild # Rebuilds network settings for an app +network:rebuildall # Rebuild network settings for all apps +``` + +The Network plugin allows developers to abstract the concept of container network management, allowing developers to both change what networks a given container is attached to as well as rebuild the configuration on the fly. + +## Usage + +### Rebuilding network settings + +There are cases where you may need to rebuild the network configuration for an app, such as on app boot or container restart. In these cases, you can use the `network:rebuild` command: + +```shell +dokku network:rebuild node-js-app +``` + +> This command will exit a non-zero number that depends on the number of containers for which configuration could not be built + +### Rebuilding all network settings + +In some cases, a docker upgrade may reset container IPs or Ports. In both cases, you can quickly rewrite those files by using the `network:rebuildall` command: + +```shell +dokku network:rebuildall +``` + +> This command will exit a non-zero number that depends on the number of containers for which configuration could not be built diff --git a/plugins/network/commands b/plugins/network/commands new file mode 100755 index 000000000..099603de0 --- /dev/null +++ b/plugins/network/commands @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +[[ " help network:help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT" +source "$PLUGIN_AVAILABLE_PATH/network/internal-functions" +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +case "$1" in + help | network:help) + network_help_cmd "$@" + ;; + + *) + exit "$DOKKU_NOT_IMPLEMENTED_EXIT" + ;; + +esac diff --git a/plugins/network/functions b/plugins/network/functions new file mode 100755 index 000000000..e10e07323 --- /dev/null +++ b/plugins/network/functions @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_AVAILABLE_PATH/proxy/functions" + +network-write-config() { + declare desc="writes the network config files out" + declare APP="$1"; verify_app_name "$APP" + local DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE" + + dokku_log_info1 "Ensuring network configuration is in sync for ${APP}" + local line; local PROC_TYPE; local PROC_COUNT; local CONTAINER_INDEX + while read -r line || [[ -n "$line" ]]; do + [[ "$line" =~ ^#.* ]] && continue + line="$(strip_inline_comments "$line")" + PROC_TYPE=${line%%=*} + PROC_COUNT=${line#*=} + CONTAINER_INDEX=1 + while [[ $CONTAINER_INDEX -le $PROC_COUNT ]]; do + local CID="" p="" port="" ipaddr="" CONTAINER_STATUS + local DOKKU_CONTAINER_ID_FILE="$DOKKU_ROOT/$APP/CONTAINER.$PROC_TYPE.$CONTAINER_INDEX" + local DOKKU_IP_FILE="$DOKKU_ROOT/$APP/IP.$PROC_TYPE.$CONTAINER_INDEX" + local DOKKU_PORT_FILE="$DOKKU_ROOT/$APP/PORT.$PROC_TYPE.$CONTAINER_INDEX" + local CONTAINER_INDEX=$(( CONTAINER_INDEX + 1 )) + + [[ -f "$DOKKU_CONTAINER_ID_FILE" ]] || continue + + CID="$(cat "$DOKKU_CONTAINER_ID_FILE")" + [[ -n "$CID" ]] || continue + CONTAINER_RUNNING="$(docker inspect -f '{{.State.Running}}' "$CID" 2>/dev/null || true)" + [[ "$CONTAINER_RUNNING" == "true" ]] || continue + + ipaddr=$(network-get-container-ipaddr "$APP" "PROC_TYPE" "$CID") + port=$(network-get-container-port "$APP" "PROC_TYPE" "$CID") + + [[ -n "$ipaddr" ]] && echo "$ipaddr" > "$DOKKU_IP_FILE" + [[ -n "$port" ]] && echo "$port" > "$DOKKU_PORT_FILE" + done + done < "$DOKKU_SCALE_FILE" +} + +network-compute-container-ports() { + declare desc="Return the ipaddr for a given app container" + declare APP="$1" PROC_TYPE="$2" + local PORTS + + if [[ "$PROC_TYPE" != "web" ]]; thren + return + fi + + is_image_herokuish_based "$IMAGE" && local DOKKU_HEROKUISH=true + + if [[ -z "$DOKKU_HEROKUISH" ]]; then + local DOKKU_DOCKERFILE_PORTS=($(config_get "$APP" DOKKU_DOCKERFILE_PORTS || true)) + fi + + if [[ -z "${DOKKU_DOCKERFILE_PORTS[*]}" ]]; then + local PORTS=5000 + else + local p + for p in ${DOKKU_DOCKERFILE_PORTS[*]};do + if [[ ! "$p" =~ .*udp.* ]]; then + # set port to first non-udp port + local p=${p//\/tcp} + local PORT=${PORT:="$p"} + fi + PORTS+=" $PORT " + done + fi + + echo "$PORTS" +} + + +network-get-container-ipaddr() { + declare desc="Return the ipaddr for a given app container" + declare APP="$1" PROC_TYPE="$2" CONTAINER_ID="$3" + local DOKKU_IS_APP_PROXY_ENABLED + local IP_ADDRESS=127.0.0.1 + + if [[ "$PROC_TYPE" != "web" ]]; thren + return + fi + + local DOKKU_IS_APP_PROXY_ENABLED="$(is_app_proxy_enabled "$APP")" + if [[ "$DOKKU_IS_APP_PROXY_ENABLED" == "true" ]]; then + IP_ADDRESS="$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$CID")" + # Docker < 1.9 compatibility + if [[ -z $ipaddr ]]; then + IP_ADDRESS=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' "$CID") + fi + fi + + echo "$IP_ADDRESS" +} + +network-get-container-port() { + declare desc="Return the ipaddr for a given app container" + declare APP="$1" PROC_TYPE="$2" CID="$3" + local DOKKU_IS_APP_PROXY_ENABLED + local PORT=5000 + + if [[ "$PROC_TYPE" != "web" ]]; thren + return + fi + + local IMAGE_TAG="$(get_running_image_tag "$APP")" + local IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG") + + is_image_herokuish_based "$IMAGE" && local DOKKU_HEROKUISH=true + + if [[ -z "$DOKKU_HEROKUISH" ]]; then + local DOKKU_DOCKERFILE_PORTS=($(config_get "$APP" DOKKU_DOCKERFILE_PORTS || true)) + fi + + if [[ -n "${DOKKU_DOCKERFILE_PORTS[*]}" ]]; then + for p in ${DOKKU_DOCKERFILE_PORTS[*]};do + if [[ ! "$p" =~ .*udp.* ]]; then + # set port to first non-udp port + p=${p//\/tcp} + PORT=${port:="$p"} + fi + done + fi + + if [[ "$DOKKU_IS_APP_PROXY_ENABLED" != "true" ]]; then + PORT="$(docker port "$CID" "$PORT" | sed 's/[0-9.]*://')" + fi + + echo "$PORT" +} diff --git a/plugins/network/internal-functions b/plugins/network/internal-functions new file mode 100755 index 000000000..31d4adf57 --- /dev/null +++ b/plugins/network/internal-functions @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +network_help_content_func() { + declare desc="return network plugin help content" + cat<, Rebuilds network settings for an app + network:rebuildall, Rebuild network settings for all apps +help_content +} + +network_help_cmd() { + if [[ $1 = "network:help" ]] ; then + echo -e 'Usage: dokku network[:COMMAND]' + echo '' + echo 'Rebuilds network settings for an app.' + echo '' + echo 'Additional commands:' + network_help_content_func | sort | column -c2 -t -s, + elif [[ $(ps -o command= $PPID) == *"--all"* ]]; then + network_help_content_func + else + cat<