Merge pull request #4719 from dokku/4665-route-internal

Add support for routing an app to a specified host:port
This commit is contained in:
Jose Diaz-Gonzalez
2021-08-06 11:11:41 -04:00
committed by GitHub
18 changed files with 298 additions and 7 deletions

View File

@@ -0,0 +1,15 @@
# Null Builder
> New as of 0.25.0
The `null` builder does nothing, and is useful for routing to services not managed by Dokku. It should not be used in normal operation. Please see the [network documentation](/docs/networking/network.md#routing-an-app-to-a-known-ip:port-combination) for more information on the aforementioned use case.
## Usage
### Detection
This builder is _never_ auto-detected. The builder _must_ be specified via the `builder:set` command:
```shell
dokku builder:set node-js-app selected null
```

View File

@@ -0,0 +1,15 @@
# Null Scheduler
> New as of 0.25.0
The `null` scheduler does nothing, and is useful for routing to services not managed by Dokku. It should not be used in normal operation. Please see the [network documentation](/docs/networking/network.md#routing-an-app-to-a-known-ip:port-combination) for more information on the aforementioned use case.
## Usage
### Detection
This scheduler is _never_ auto-detected. The scheduler _must_ be specified via the `config:set` command:
```shell
dokku config:set node-js-app DOCKER_SCHEDULER=null
```

View File

@@ -940,6 +940,21 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
# TODO
```
### `network-get-static-listeners`
- Description: Return the network value for an app's property
- Invoked by: `internally triggered by proxy-build-config`
- Arguments: `$APP $PROCESS_TYPE`
- Example:
```shell
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
# TODO
```
### `network-write-ipaddr`
- Description: Write the ipaddr for a given app index

View File

@@ -123,6 +123,40 @@ dokku network:info test-network
// TODO
```
### Routing an app to a known ip:port combination
> New as of 0.25.0
In some cases, it may be necessary to route an app to an existing `$IP:$PORT` combination. This is particularly the case for internal admin tools or services that aren't run by Dokku but have a web ui that would benefit from being exposed by Dokku. This can be done by setting a value for `static-web-lister` and running a few other commands when creating an app.
```shell
# for a service listening on:
# - ip address: 127.0.0.1
# - port: 8080
# create the app
dokku apps:create local-app
# set the builder to the null builder, which does nothing
dokku builder:set local-app selected null
# set the scheduler to the null scheduler, which does nothing
dokku config:set local-app DOKKU_SCHEDULER=null
# set the static-web-listener network property to the ip:port combination for your app.
dokku network:set local-app static-web-listener 127.0.0.1:8080
# set the port map as desired for the port specified in your static-web-listener
dokku proxy:ports-set local-app http:80:8080
# set the domains desired
dokku domains:set local-app local-app.dokku.me
dokku proxy:build-config local-app
```
Only a single `$IP:$PORT` combination can be routed to for a given app, and that `$IP:$PORT` combination _must_ be accessible to the proxy, or requests to the app may not resolve.
### Attaching an app to a network
> New as of 0.20.0, Requires Docker 1.21+

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 "$@"

View File

@@ -0,0 +1,52 @@
#!/usr/bin/env bats
load test_helper
setup() {
global_setup
create_app
}
teardown() {
destroy_app
global_teardown
}
# covers https://github.com/dokku/dokku/issues/4665
@test "(proxied-app) default" {
run /bin/bash -c "dokku builder:set $TEST_APP selected null"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku config:set $TEST_APP DOKKU_SCHEDULER=null"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku network:set $TEST_APP static-web-listener 127.0.0.1:8080"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku proxy:ports-set $TEST_APP http:80:8080"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku domains:set $TEST_APP $TEST_APP.dokku.me"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku proxy:build-config $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku nginx:show-config $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "server 127.0.0.1:8080;"
}