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:
15
docs/deployment/builders/null.md
Normal file
15
docs/deployment/builders/null.md
Normal 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
|
||||
```
|
||||
15
docs/deployment/schedulers/null.md
Normal file
15
docs/deployment/schedulers/null.md
Normal 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
|
||||
```
|
||||
@@ -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
|
||||
|
||||
@@ -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
16
plugins/builder-null/commands
Executable 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
|
||||
23
plugins/builder-null/help-functions
Executable file
23
plugins/builder-null/help-functions
Executable 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
|
||||
}
|
||||
4
plugins/builder-null/plugin.toml
Normal file
4
plugins/builder-null/plugin.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder-null plugin"
|
||||
version = "0.24.10"
|
||||
[plugin.config]
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
16
plugins/scheduler-null/commands
Executable 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
|
||||
23
plugins/scheduler-null/help-functions
Executable file
23
plugins/scheduler-null/help-functions
Executable 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
|
||||
}
|
||||
4
plugins/scheduler-null/plugin.toml
Normal file
4
plugins/scheduler-null/plugin.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core scheduler-null plugin"
|
||||
version = "0.24.10"
|
||||
[plugin.config]
|
||||
17
plugins/scheduler-null/scheduler-is-deployed
Executable file
17
plugins/scheduler-null/scheduler-is-deployed
Executable 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 "$@"
|
||||
52
tests/unit/proxied-app.bats
Normal file
52
tests/unit/proxied-app.bats
Normal 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;"
|
||||
}
|
||||
Reference in New Issue
Block a user