feat: add support for routing an app to a specified host:port

This is useful when there is a service not managed by Dokku but should be exposed via the Dokku routing layer. As an example, some binaries (consul, nomad, vault) expose web uis, and are traditionally run on the host directly vs in a container.

Closes #4665
This commit is contained in:
Jose Diaz-Gonzalez
2021-08-06 01:29:25 -04:00
parent afd64c3e04
commit 9ecfa843f0
18 changed files with 298 additions and 7 deletions

16
plugins/builder-null/commands Executable file
View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
[[ " help builder-null:help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
source "$PLUGIN_AVAILABLE_PATH/builder-null/help-functions"
set -eo pipefail
[[ $DOKKU_TRACE ]] && set -x
case "$1" in
help | builder-null:help)
cmd-builder-null-help "$@"
;;
*)
exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
;;
esac

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -eo pipefail
[[ $DOKKU_TRACE ]] && set -x
cmd-builder-null-help() {
declare desc="help command"
declare CMD="$1"
local plugin_name="builder-null"
local plugin_description="No-op builder plugin"
if [[ "$CMD" == "${plugin_name}:help" ]]; then
echo -e "Usage: dokku ${plugin_name}[:COMMAND]"
echo ''
echo "$plugin_description"
echo ''
elif [[ $(ps -o command= $PPID) == *"--all"* ]]; then
true
else
cat <<help_desc
$plugin_name, $plugin_description
help_desc
fi
}

View File

@@ -0,0 +1,4 @@
[plugin]
description = "dokku core builder-null plugin"
version = "0.24.10"
[plugin.config]

View File

@@ -1,5 +1,5 @@
SUBCOMMANDS = subcommands/create subcommands/destroy subcommands/exists subcommands/info subcommands/list subcommands/rebuild subcommands/rebuildall subcommands/report subcommands/set
TRIGGERS = triggers/core-post-deploy triggers/docker-args-process-build triggers/docker-args-process-deploy triggers/docker-args-process-run triggers/install triggers/network-build-config triggers/network-compute-ports triggers/network-config-exists triggers/network-get-ipaddr triggers/network-get-listeners triggers/network-get-port triggers/network-get-property triggers/network-write-ipaddr triggers/network-write-port triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-container-create triggers/post-create triggers/post-delete triggers/report
TRIGGERS = triggers/core-post-deploy triggers/docker-args-process-build triggers/docker-args-process-deploy triggers/docker-args-process-run triggers/install triggers/network-build-config triggers/network-compute-ports triggers/network-config-exists triggers/network-get-ipaddr triggers/network-get-listeners triggers/network-get-port triggers/network-get-property triggers/network-get-static-listeners triggers/network-write-ipaddr triggers/network-write-port triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-container-create triggers/post-create triggers/post-delete triggers/report
BUILD = commands subcommands triggers
PLUGIN_NAME = network

View File

@@ -3,6 +3,7 @@ package network
import (
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"strconv"
@@ -17,18 +18,19 @@ import (
var (
// DefaultProperties is a map of all valid network properties with corresponding default property values
DefaultProperties = map[string]string{
"bind-all-interfaces": "",
"attach-post-create": "",
"attach-post-deploy": "",
"bind-all-interfaces": "",
"initial-network": "",
"static-web-listener": "",
"tld": "",
}
// GlobalProperties is a map of all valid global network properties
GlobalProperties = map[string]bool{
"bind-all-interfaces": true,
"attach-post-create": true,
"attach-post-deploy": true,
"bind-all-interfaces": true,
"initial-network": true,
"tld": true,
}
@@ -40,6 +42,10 @@ func BuildConfig(appName string) error {
return nil
}
if staticWebListener := reportStaticWebListener(appName); staticWebListener != "" {
return nil
}
appRoot := common.AppRoot(appName)
s, err := common.PlugnTriggerOutput("ps-current-scale", []string{appName}...)
if err != nil {
@@ -97,6 +103,22 @@ func BuildConfig(appName string) error {
// GetContainerIpaddress returns the ipaddr for a given app container
func GetContainerIpaddress(appName, processType, containerID string) (ipAddr string) {
if processType == "web" {
if staticWebListener := reportStaticWebListener(appName); staticWebListener != "" {
ip, _, err := net.SplitHostPort(staticWebListener)
if err == nil {
return ip
}
ip2 := net.ParseIP(staticWebListener)
if ip2 != nil {
return ip2.String()
}
return "127.0.0.1"
}
}
if b, err := common.DockerInspect(containerID, "{{ .HostConfig.NetworkMode }}"); err == nil {
if string(b[:]) == "host" {
return "127.0.0.1"
@@ -123,6 +145,17 @@ func GetContainerIpaddress(appName, processType, containerID string) (ipAddr str
// GetContainerPort returns the port for a given app container
func GetContainerPort(appName, processType string, containerID string, isHerokuishContainer bool) (port string) {
if processType == "web" {
if staticWebListener := reportStaticWebListener(appName); staticWebListener != "" {
_, port, err := net.SplitHostPort(staticWebListener)
if err == nil {
return port
}
return "80"
}
}
dockerfilePorts := make([]string, 0)
if !isHerokuishContainer {
configValue := config.GetWithDefault(appName, "DOKKU_DOCKERFILE_PORTS", "")
@@ -156,6 +189,12 @@ func GetContainerPort(appName, processType string, containerID string, isHerokui
// GetListeners returns a string array of app listeners
func GetListeners(appName string, processType string) []string {
if processType == "web" {
if staticWebListener := reportStaticWebListener(appName); staticWebListener != "" {
return []string{staticWebListener}
}
}
appRoot := common.AppRoot(appName)
ipPrefix := fmt.Sprintf("/IP.%s.", processType)

View File

@@ -27,6 +27,7 @@ func ReportSingleApp(appName string, format string, infoFlag string) error {
"--network-global-initial-network": reportGlobalInitialNetwork,
"--network-global-tld": reportGlobalTld,
"--network-initial-network": reportInitialNetwork,
"--network-static-web-listener": reportStaticWebListener,
"--network-tld": reportTld,
"--network-web-listeners": reportWebListeners,
}
@@ -123,6 +124,10 @@ func reportInitialNetwork(appName string) string {
return common.PropertyGet("network", appName, "initial-network")
}
func reportStaticWebListener(appName string) string {
return common.PropertyGet("network", appName, "static-web-listener")
}
func reportTld(appName string) string {
return common.PropertyGet("network", appName, "tld")
}

View File

@@ -59,6 +59,10 @@ func main() {
appName := flag.Arg(0)
property := flag.Arg(1)
err = network.TriggerNetworkGetProperty(appName, property)
case "network-get-static-listeners":
appName := flag.Arg(0)
processType := flag.Arg(1)
err = network.TriggerNetworkGetStaticListeners(appName, processType)
case "network-write-ipaddr":
appName := flag.Arg(0)
processType := flag.Arg(1)

View File

@@ -140,6 +140,13 @@ func TriggerNetworkGetProperty(appName string, property string) error {
return nil
}
// TriggerNetworkGetStaticListeners fetches the static listener for the specified app/processType combination
func TriggerNetworkGetStaticListeners(appName string, processType string) error {
staticWebListener := reportStaticWebListener(appName)
fmt.Println(staticWebListener)
return nil
}
// TriggerNetworkWriteIpaddr writes the ip to disk
func TriggerNetworkWriteIpaddr(appName string, processType string, containerIndex string, ip string) error {
appRoot := common.AppRoot(appName)

View File

@@ -447,10 +447,12 @@ nginx_build_config() {
if [[ -z "$DOKKU_APP_LISTENERS" ]]; then
dokku_log_warn_quiet "No web listeners specified for $APP"
elif (is_deployed "$APP"); then
local IMAGE_TAG=$(get_running_image_tag "$APP")
local IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG" 2>/dev/null)
if ! verify_image "$IMAGE" 2>/dev/null; then
dokku_log_fail "Missing image for app"
if [[ "$(plugn trigger network-get-static-listeners "$APP" "web")" == "" ]]; then
local IMAGE_TAG=$(get_running_image_tag "$APP")
local IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG" 2>/dev/null)
if ! verify_image "$IMAGE" 2>/dev/null; then
dokku_log_fail "Missing image for app"
fi
fi
local NGINX_BUILD_CONFIG_TMP_WORK_DIR=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX")

16
plugins/scheduler-null/commands Executable file
View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
[[ " help scheduler-null:help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
source "$PLUGIN_AVAILABLE_PATH/scheduler-null/help-functions"
set -eo pipefail
[[ $DOKKU_TRACE ]] && set -x
case "$1" in
help | scheduler-null:help)
cmd-scheduler-null-help "$@"
;;
*)
exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
;;
esac

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -eo pipefail
[[ $DOKKU_TRACE ]] && set -x
cmd-scheduler-null-help() {
declare desc="help command"
declare CMD="$1"
local plugin_name="scheduler-null"
local plugin_description="No-op scheduler plugin"
if [[ "$CMD" == "${plugin_name}:help" ]]; then
echo -e "Usage: dokku ${plugin_name}[:COMMAND]"
echo ''
echo "$plugin_description"
echo ''
elif [[ $(ps -o command= $PPID) == *"--all"* ]]; then
true
else
cat <<help_desc
$plugin_name, $plugin_description
help_desc
fi
}

View File

@@ -0,0 +1,4 @@
[plugin]
description = "dokku core scheduler-null plugin"
version = "0.24.10"
[plugin.config]

View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -eo pipefail
[[ $DOKKU_TRACE ]] && set -x
trigger-scheduler-null-scheduler-is-deployed() {
declare desc="checks if an app is deployed"
declare trigger="scheduler-is-deployed"
declare DOKKU_SCHEDULER="$1" APP="$2"
if [[ "$DOKKU_SCHEDULER" != "null" ]]; then
return
fi
return 0
}
trigger-scheduler-null-scheduler-is-deployed "$@"