feat: allow users to bind to all interfaces when a proxy is enabled

- use a plugin trigger to see whether we should bind to all interfaces
- create a generic way of setting properties for a plugin
- migrate proxy-enabled to the new network property "bind-all-interfaces"
- add network:set subcommand
This commit is contained in:
Jose Diaz-Gonzalez
2017-09-03 20:36:09 -04:00
committed by Michael Hobbs
parent 7a4464a8ae
commit acde3d60a7
19 changed files with 477 additions and 53 deletions

View File

@@ -419,6 +419,21 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
# TODO # TODO
``` ```
### `network-get-property`
- Description: Return the network value for an application's property
- Invoked by: `internally triggered by a deploy`
- Arguments: `$APP $KEY`
- Example:
```shell
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
# TODO
```
### `network-get-port` ### `network-get-port`
- Description: Return the port for a given app container - Description: Return the port for a given app container

View File

@@ -1,10 +1,11 @@
# Network Management # Network Management
> New as of 0.10.0 > New as of 0.11.0
``` ```
network:rebuild <app> # Rebuilds network settings for an app network:rebuild <app> # Rebuilds network settings for an app
network:rebuildall # Rebuild network settings for all apps network:rebuildall # Rebuild network settings for all apps
network:set <app> <key> (<value>) # Set or clear a network property for an app
``` ```
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. 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.
@@ -30,3 +31,55 @@ 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 > This command will exit a non-zero number that depends on the number of containers for which configuration could not be built
### Container network interface binding
> This functionality does not control the `--network` docker flag. Please use the [docker-options plugin](docs/advanced-usage/docker-options.md) to manage this flag.
By default, an application will only bind to the internal interface. This behavior can be modified per app by changing the `bind-all-interfaces` network property.
```shell
# bind to the default docker interface (`docker0`) with a random internal ip
# this is the default behavior
dokku network:set node-js-app bind-all-interfaces false
# bind to all interfaces (`0.0.0.0`) on a random port for each upstream port
# this will make the app container directly accessible by other hosts on your network
# ports are randomized for every deploy, e.g. `0.0.0.0:32771->5000/tcp`.
dokku network:set node-js-app bind-all-interfaces true
```
By way of example, in the default case, each container is bound to the docker interface:
```shell
docker ps
```
```
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1b88d8aec3d1 dokku/node-js-app:latest "/bin/bash -c '/star About a minute ago Up About a minute node-js-app.web.1
```
As such, the container's IP address will be an internal IP, and thus it is only accessible on the host itself:
```
docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' node-js-app.web.1
```
```
172.17.0.6
```
However, you can disable the internal proxying via the `network:set` command so that it will listen on the host's IP address:
```shell
dokku network:set node-js-app bind-all-interfaces true
# container bound to all interfaces
docker ps
```
```
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d6499edb0edb dokku/node-js-app:latest "/bin/bash -c '/star About a minute ago Up About a minute 0.0.0.0:49153->5000/tcp node-js-app.web.1
```

View File

@@ -19,46 +19,9 @@ In Dokku 0.5.0, port proxying was decoupled from the `nginx-vhosts` plugin into
### Container network interface binding ### Container network interface binding
> New as of 0.5.0 > Changed as of 0.11.0
By default, the deployed docker container running your app's web process will bind to the internal docker network interface (i.e. `docker inspect --format '{{ .NetworkSettings.IPAddress }}' $CONTAINER_ID`). This behavior can be modified per app by disabling the proxy (i.e. `dokku proxy:disable <app>`). In this case, the container will bind to an external interface (i.e. `0.0.0.0`) and your app container will be directly accessible by other hosts on your network. From Dokku versions `0.5.0` until `0.11.0`, enabling or disabling an application's proxy would **also** control whether or not the application was bound to all interfaces - e.g. `0.0.0.0`. As of `0.10.0`, this is now controlled by the network plugin. Please see the [network documentation](/docs/networking/network.md#container-network-interface-binding) for more information.
> If a proxy is disabled, Dokku will bind your container's port to a random port on the host for every deploy, e.g. `0.0.0.0:32771->5000/tcp`.
By way of example, in the default case, each container is bound to the docker interface:
```shell
docker ps
```
```
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1b88d8aec3d1 dokku/node-js-app:latest "/bin/bash -c '/star About a minute ago Up About a minute node-js-app.web.1
```
As such, the container's IP address will be an internal IP, and thus it is only accessible on the host itself:
```
docker inspect --format '{{ .NetworkSettings.IPAddress }}' node-js-app.web.1
```
```
172.17.0.6
```
However, you can disable the internal proxying via the `proxy:disable` command so that it will listen on the host's IP address:
```shell
dokku proxy:disable node-js-app
# container bound to all interfaces
docker ps
```
```
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d6499edb0edb dokku/node-js-app:latest "/bin/bash -c '/star About a minute ago Up About a minute 0.0.0.0:49153->5000/tcp node-js-app.web.1
```
### Displaying proxy reports about an app ### Displaying proxy reports about an app

View File

@@ -325,11 +325,11 @@ func VerifyAppName(appName string) (err error) {
dokkuRoot := MustGetEnv("DOKKU_ROOT") dokkuRoot := MustGetEnv("DOKKU_ROOT")
appRoot := strings.Join([]string{dokkuRoot, appName}, "/") appRoot := strings.Join([]string{dokkuRoot, appName}, "/")
if !DirectoryExists(appRoot) { if !DirectoryExists(appRoot) {
return fmt.Errorf("App %s does not exist: %v\n", appName, err) return fmt.Errorf("app %s does not exist: %v", appName, err)
} }
r, _ := regexp.Compile("^[a-z].*") r, _ := regexp.Compile("^[a-z].*")
if !r.MatchString(appName) { if !r.MatchString(appName) {
return fmt.Errorf("App name (%s) must begin with lowercase alphanumeric character\n", appName) return fmt.Errorf("app name (%s) must begin with lowercase alphanumeric character", appName)
} }
return err return err
} }

View File

@@ -538,11 +538,10 @@ dokku_deploy_cmd() {
local cmd="deploy" local cmd="deploy"
source "$PLUGIN_AVAILABLE_PATH/checks/functions" source "$PLUGIN_AVAILABLE_PATH/checks/functions"
source "$PLUGIN_AVAILABLE_PATH/config/functions" source "$PLUGIN_AVAILABLE_PATH/config/functions"
source "$PLUGIN_AVAILABLE_PATH/proxy/functions"
[[ -z $1 ]] && dokku_log_fail "Please specify an app to run the command on" [[ -z $1 ]] && dokku_log_fail "Please specify an app to run the command on"
local APP="$1" IMAGE_TAG="$2" local APP="$1" IMAGE_TAG="$2"
local DOKKU_HEROKUISH IMAGE local DOKKU_DOCKER_STOP_TIMEOUT DOKKU_HEROKUISH DOKKU_NETWORK_BIND_ALL IMAGE
DOKKU_HEROKUISH=false DOKKU_HEROKUISH=false
IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG") IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG")
verify_app_name "$APP" verify_app_name "$APP"
@@ -552,8 +551,8 @@ dokku_deploy_cmd() {
local DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE" local DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE"
local oldids=$(get_app_container_ids "$APP") local oldids=$(get_app_container_ids "$APP")
local DOKKU_IS_APP_PROXY_ENABLED="$(is_app_proxy_enabled "$APP")" DOKKU_NETWORK_BIND_ALL="$(plugn trigger network-get-property "$APP" bind-all-interfaces)"
local DOKKU_DOCKER_STOP_TIMEOUT="$(config_get "$APP" DOKKU_DOCKER_STOP_TIMEOUT || true)" DOKKU_DOCKER_STOP_TIMEOUT="$(config_get "$APP" DOKKU_DOCKER_STOP_TIMEOUT || true)"
[[ $DOKKU_DOCKER_STOP_TIMEOUT ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" [[ $DOKKU_DOCKER_STOP_TIMEOUT ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}"
local line; local PROC_TYPE; local PROC_COUNT; local CONTAINER_INDEX local line; local PROC_TYPE; local PROC_COUNT; local CONTAINER_INDEX
@@ -605,7 +604,7 @@ dokku_deploy_cmd() {
DOKKU_DOCKER_PORT_ARGS+=" -p $p " DOKKU_DOCKER_PORT_ARGS+=" -p $p "
done done
if [[ "$DOKKU_IS_APP_PROXY_ENABLED" == "true" ]]; then if [[ "$DOKKU_NETWORK_BIND_ALL" == "false" ]]; then
# shellcheck disable=SC2086 # shellcheck disable=SC2086
cid=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d -e PORT=$DOKKU_PORT $DOCKER_ARGS $IMAGE $START_CMD) cid=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d -e PORT=$DOKKU_PORT $DOCKER_ARGS $IMAGE $START_CMD)
else else

View File

@@ -36,10 +36,14 @@ func LogInfo2Quiet(text string) {
} }
} }
// LogVerbose is the verbose log formatter
// prints indented text to stdout
func LogVerbose(text string) { func LogVerbose(text string) {
fmt.Fprintln(os.Stdout, fmt.Sprintf(" %s", text)) fmt.Fprintln(os.Stdout, fmt.Sprintf(" %s", text))
} }
// LogVerboseQuiet is the verbose log formatter
// prints indented text to stdout (with quiet option)
func LogVerboseQuiet(text string) { func LogVerboseQuiet(text string) {
if os.Getenv("DOKKU_QUIET_OUTPUT") != "" { if os.Getenv("DOKKU_QUIET_OUTPUT") != "" {
LogVerbose(text) LogVerbose(text)

View File

@@ -0,0 +1,181 @@
package common
import (
"fmt"
"io/ioutil"
"os"
"os/user"
"reflect"
"strconv"
"strings"
)
// CommandPropertySet is a generic function that will set a property for a given plugin/app combination
func CommandPropertySet(pluginName string, appName string, property string, value string, validProperties map[string]bool) {
err := VerifyAppName(appName)
if err != nil {
LogFail(err.Error())
}
if property == "" {
LogFail("No property specified")
}
if !isValidProperty(validProperties, property) {
properties := reflect.ValueOf(validProperties).MapKeys()
validPropertyList := make([]string, len(properties))
for i := 0; i < len(properties); i++ {
validPropertyList[i] = properties[i].String()
}
LogFail(fmt.Sprintf("Invalid property specified, valid properties include: %s", strings.Join(validPropertyList, ", ")))
}
if value != "" {
LogInfo2Quiet(fmt.Sprintf("Setting %s to %s", property, value))
PropertyWrite(pluginName, appName, property, value)
} else {
LogInfo2Quiet(fmt.Sprintf("Unsetting %s", property))
PropertyDelete(pluginName, appName, property)
}
}
// PropertyDelete deletes a property from the plugin properties for an app
func PropertyDelete(pluginName string, appName string, property string) {
pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
propertyPath := strings.Join([]string{pluginAppConfigRoot, property}, "/")
err := os.Remove(propertyPath)
if err != nil {
LogFail(fmt.Sprintf("Unable to remove %s property %s.%s", pluginName, appName, property))
}
}
// PropertyDestroy destroys the plugin properties for an app
func PropertyDestroy(pluginName string, appName string) {
if appName == "_all_" {
pluginConfigPath := getPluginConfigPath(pluginName)
os.RemoveAll(pluginConfigPath)
} else {
pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
os.RemoveAll(pluginAppConfigRoot)
}
}
// PropertyExists returns whether a property exists or not
func PropertyExists(pluginName string, appName string, property string) bool {
pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
propertyPath := strings.Join([]string{pluginAppConfigRoot, property}, "/")
_, err := os.Stat(propertyPath)
return !os.IsNotExist(err)
}
// PropertyGet returns the value for a given property
func PropertyGet(pluginName string, appName string, property string) string {
return PropertyGetDefault(pluginName, appName, property, "")
}
// PropertyGetDefault returns the value for a given property with a specified default value
func PropertyGetDefault(pluginName string, appName string, property string, defaultValue string) string {
if !PropertyExists(pluginName, appName, property) {
return ""
}
pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
propertyPath := strings.Join([]string{pluginAppConfigRoot, property}, "/")
b, err := ioutil.ReadFile(propertyPath)
if err != nil {
LogWarn(fmt.Sprintf("Unable to read %s property %s.%s", pluginName, appName, property))
return ""
}
return string(b)
}
// PropertyWrite writes a value for a given application property
func PropertyWrite(pluginName string, appName string, property string, value string) {
err := makePropertyPath(pluginName, appName)
if err != nil {
LogFail(fmt.Sprintf("Unable to create %s config directory for %s: %s", pluginName, appName, err.Error()))
}
pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
propertyPath := strings.Join([]string{pluginAppConfigRoot, property}, "/")
file, err := os.Create(propertyPath)
if err != nil {
LogFail(fmt.Sprintf("Unable to write %s config value %s.%s: %s", pluginName, appName, property, err.Error))
}
defer file.Close()
fmt.Fprintf(file, value)
file.Chmod(0600)
setPermissions(propertyPath, 0600)
}
// PropertySetup creates the plugin config root
func PropertySetup(pluginName string) error {
pluginConfigRoot := getPluginConfigPath(pluginName)
err := os.MkdirAll(pluginConfigRoot, 0755)
if err != nil {
return err
}
return setPermissions(pluginConfigRoot, 0755)
}
// isValidProperty returns whether a property is a valid property or not
func isValidProperty(validProperties map[string]bool, property string) bool {
return validProperties[property]
}
// getPluginAppPropertyPath returns the plugin property path for a given plugin/app combination
func getPluginAppPropertyPath(pluginName string, appName string) string {
return strings.Join([]string{getPluginConfigPath(pluginName), appName}, "/")
}
// getPluginConfigPath returns the plugin property path for a given plugin
func getPluginConfigPath(pluginName string) string {
return strings.Join([]string{MustGetEnv("DOKKU_LIB_ROOT"), "config", pluginName}, "/")
}
// makePropertyPath ensures that a property path exists
func makePropertyPath(pluginName string, appName string) error {
pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
err := os.MkdirAll(pluginAppConfigRoot, 0755)
if err != nil {
return err
}
return setPermissions(pluginAppConfigRoot, 0755)
}
// setPermissions sets the proper owner and filemode for a given file
func setPermissions(path string, fileMode os.FileMode) error {
err := os.Chmod(path, fileMode)
if err != nil {
return err
}
group, err := user.LookupGroup("dokku")
if err != nil {
return err
}
user, err := user.Lookup("dokku")
if err != nil {
return err
}
uid, err := strconv.Atoi(user.Uid)
if err != nil {
return err
}
gid, err := strconv.Atoi(group.Gid)
if err != nil {
return err
}
err = os.Chown(path, uid, gid)
if err != nil {
return err
}
return nil
}

View File

@@ -22,7 +22,7 @@ func GetWithDefault(appName string, key string, defaultValue string) string {
continue continue
} }
value = strings.TrimPrefix(line, prefix) value = strings.TrimPrefix(line, prefix)
if strings.HasPrefix(line, "'") && strings.HasSuffix(line, "'") { if strings.HasPrefix(value, "'") && strings.HasSuffix(value, "'") {
value = strings.TrimPrefix(strings.TrimSuffix(value, "'"), "'") value = strings.TrimPrefix(strings.TrimSuffix(value, "'"), "'")
} }
} }

View File

@@ -1,3 +1,6 @@
/commands /commands
/subcommands/* /subcommands/*
/network-* /network-*
/install
/post-create
/post-delete

View File

@@ -8,17 +8,23 @@ build-in-docker: clean
bash -c "make build" || exit $$? bash -c "make build" || exit $$?
build: commands subcommands triggers build: commands subcommands triggers
subcommands: subcommands/rebuild subcommands/rebuildall subcommands: subcommands/rebuild subcommands/rebuildall subcommands/set
triggers: network-build-config network-compute-ports network-config-exists network-get-ipaddr network-get-listeners network-get-port network-write-ipaddr network-write-port triggers: install network-build-config network-compute-ports network-config-exists network-get-ipaddr network-get-listeners network-get-port network-get-property network-write-ipaddr network-write-port post-create post-delete
commands: **/**/commands.go commands: **/**/commands.go
go build -a -o commands src/commands/commands.go go build -a -o commands src/commands/commands.go
subcommands/rebuild: **/**/**/rebuild.go subcommands/rebuild: **/**/**/rebuild.go
go build -a -o subcommands/rebuild src/subcommands/rebuild/rebuild.go go build -a -o subcommands/rebuild src/subcommands/rebuild/rebuild.go
subcommands/rebuildall: **/**/**/rebuildall.go subcommands/rebuildall: **/**/**/rebuildall.go
go build -a -o subcommands/rebuildall src/subcommands/rebuildall/rebuildall.go go build -a -o subcommands/rebuildall src/subcommands/rebuildall/rebuildall.go
subcommands/set: **/**/**/set.go
go build -a -o subcommands/set src/subcommands/set/set.go
install: **/**/**/install.go
go build -a -o install src/triggers/install/install.go
network-build-config: **/**/**/network-build-config.go network-build-config: **/**/**/network-build-config.go
go build -a -o network-build-config src/triggers/network-build-config/network-build-config.go go build -a -o network-build-config src/triggers/network-build-config/network-build-config.go
@@ -37,14 +43,23 @@ network-get-listeners: **/**/**/network-get-listeners.go
network-get-port: **/**/**/network-get-port.go network-get-port: **/**/**/network-get-port.go
go build -a -o network-get-port src/triggers/network-get-port/network-get-port.go go build -a -o network-get-port src/triggers/network-get-port/network-get-port.go
network-get-property: **/**/**/network-get-property.go
go build -a -o network-get-property src/triggers/network-get-property/network-get-property.go
network-write-ipaddr: **/**/**/network-write-ipaddr.go network-write-ipaddr: **/**/**/network-write-ipaddr.go
go build -a -o network-write-ipaddr src/triggers/network-write-ipaddr/network-write-ipaddr.go go build -a -o network-write-ipaddr src/triggers/network-write-ipaddr/network-write-ipaddr.go
network-write-port: **/**/**/network-write-port.go network-write-port: **/**/**/network-write-port.go
go build -a -o network-write-port src/triggers/network-write-port/network-write-port.go go build -a -o network-write-port src/triggers/network-write-port/network-write-port.go
post-create: **/**/**/post-create.go
go build -a -o post-create src/triggers/post-create/post-create.go
post-delete: **/**/**/post-delete.go
go build -a -o post-delete src/triggers/post-delete/post-delete.go
clean: clean:
rm -rf commands subcommands network-* rm -rf commands subcommands network-* install post-create post-delete
src-clean: src-clean:
rm -rf .gitignore src vendor Makefile rm -rf .gitignore src vendor Makefile

View File

@@ -12,6 +12,16 @@ import (
sh "github.com/codeskyblue/go-sh" sh "github.com/codeskyblue/go-sh"
) )
// ValidProperties is a map of all valid network properties
var ValidProperties = map[string]bool{
"bind-all-interfaces": true,
}
// DefaultPropertyValues is a map of all valid network properties with corresponding default property values
var DefaultPropertyValues = map[string]string{
"bind-all-interfaces": "false",
}
// BuildConfig builds network config files // BuildConfig builds network config files
func BuildConfig(appName string) { func BuildConfig(appName string) {
err := common.VerifyAppName(appName) err := common.VerifyAppName(appName)
@@ -147,6 +157,14 @@ func GetContainerPort(appName string, procType string, isHerokuishContainer bool
return port return port
} }
// GetDefaultValue returns the default value for a given property
func GetDefaultValue(property string) string {
if ValidProperties[property] {
return DefaultPropertyValues[property]
}
return ""
}
// HasNetworkConfig returns whether the network configuration for a given app exists // HasNetworkConfig returns whether the network configuration for a given app exists
func HasNetworkConfig(appName string) bool { func HasNetworkConfig(appName string) bool {
appRoot := strings.Join([]string{common.MustGetEnv("DOKKU_ROOT"), appName}, "/") appRoot := strings.Join([]string{common.MustGetEnv("DOKKU_ROOT"), appName}, "/")

View File

@@ -20,6 +20,7 @@ Additional commands:`
helpContent = ` helpContent = `
network:rebuild <app>, Rebuilds network settings for an app network:rebuild <app>, Rebuilds network settings for an app
network:rebuildall, Rebuild network settings for all apps network:rebuildall, Rebuild network settings for all apps
network:set <app> <property> (<value>), Set or clear a network property for an app
` `
) )

View File

@@ -0,0 +1,22 @@
package main
import (
"flag"
common "github.com/dokku/dokku/plugins/common"
network "github.com/dokku/dokku/plugins/network"
)
// set or clear a network property for an app
func main() {
flag.Parse()
appName := flag.Arg(1)
property := flag.Arg(2)
value := flag.Arg(3)
if property == "bind-all-interfaces" && value == "" {
value = "false"
}
common.CommandPropertySet("network", appName, property, value, network.ValidProperties)
}

View File

@@ -0,0 +1,33 @@
package main
import (
"fmt"
common "github.com/dokku/dokku/plugins/common"
proxy "github.com/dokku/dokku/plugins/proxy"
)
// runs the install step for the network plugin
func main() {
err := common.PropertySetup("network")
if err != nil {
common.LogFail(fmt.Sprintf("Unable to install the network plugin: %s", err.Error()))
}
apps, err := common.DokkuApps()
if err != nil {
return
}
for _, appName := range apps {
if common.PropertyExists("network", appName, "bind-all-interfaces") {
continue
}
if proxy.IsAppProxyEnabled(appName) {
common.LogVerboseQuiet("Setting %s network property 'bind-all-interfaces' to false")
common.PropertyWrite("network", appName, "bind-all-interfaces", "false")
} else {
common.LogVerboseQuiet("Setting %s network property 'bind-all-interfaces' to true")
common.PropertyWrite("network", appName, "bind-all-interfaces", "true")
}
}
}

View File

@@ -0,0 +1,18 @@
package main
import (
"flag"
common "github.com/dokku/dokku/plugins/common"
network "github.com/dokku/dokku/plugins/network"
)
// write the port to stdout for a given app container
func main() {
flag.Parse()
appName := flag.Arg(0)
property := flag.Arg(1)
defaultValue := network.GetDefaultValue(property)
common.PropertyGetDefault("network", appName, property, defaultValue)
}

View File

@@ -0,0 +1,15 @@
package main
import (
"flag"
common "github.com/dokku/dokku/plugins/common"
)
// set bind-all-interfaces to false by default
func main() {
flag.Parse()
appName := flag.Arg(0)
common.PropertyWrite("network", appName, "bind-all-interfaces", "false")
}

View File

@@ -0,0 +1,15 @@
package main
import (
"flag"
common "github.com/dokku/dokku/plugins/common"
)
// write the port to stdout for a given app container
func main() {
flag.Parse()
appName := flag.Arg(0)
common.PropertyDestroy("network", appName)
}

View File

@@ -0,0 +1,69 @@
#!/usr/bin/env bats
load test_helper
setup() {
global_setup
[[ -f "$DOKKU_ROOT/VHOST" ]] && cp -fp "$DOKKU_ROOT/VHOST" "$DOKKU_ROOT/VHOST.bak"
[[ -f "$DOKKU_ROOT/HOSTNAME" ]] && cp -fp "$DOKKU_ROOT/HOSTNAME" "$DOKKU_ROOT/HOSTNAME.bak"
create_app
}
teardown() {
destroy_app 0 $TEST_APP
[[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST"
[[ -f "$DOKKU_ROOT/HOSTNAME.bak" ]] && mv "$DOKKU_ROOT/HOSTNAME.bak" "$DOKKU_ROOT/HOSTNAME" && chown dokku:dokku "$DOKKU_ROOT/HOSTNAME"
global_teardown
}
assert_nonssl_domain() {
local domain=$1
assert_app_domain "${domain}"
assert_http_success "http://${domain}"
}
assert_app_domain() {
local domain=$1
run /bin/bash -c "dokku domains $TEST_APP 2> /dev/null | grep -xF ${domain}"
echo "output: "$output
echo "status: "$status
assert_output "${domain}"
}
assert_external_port() {
local CID="$1"; local exit_status="$2"
local EXTERNAL_PORT_COUNT=$(docker port $CID | wc -l)
run /bin/bash -c "[[ $EXTERNAL_PORT_COUNT -gt 0 ]]"
if [[ "$exit_status" == "success" ]]; then
assert_success
else
assert_failure
fi
}
@test "(proxy) network:set bind-all-interfaces" {
deploy_app
assert_nonssl_domain "${TEST_APP}.dokku.me"
run dokku network:set $TEST_APP bind-all-interfaces true
run dokku ps:rebuild $TEST_APP
echo "output: "$output
echo "status: "$status
assert_success
assert_http_success "${TEST_APP}.dokku.me"
for CID_FILE in $DOKKU_ROOT/$TEST_APP/CONTAINER.web.*; do
assert_external_port $(< $CID_FILE) success
done
run dokku network:set $TEST_APP bind-all-interfaces false
run dokku ps:rebuild $TEST_APP
echo "output: "$output
echo "status: "$status
assert_success
assert_http_success "${TEST_APP}.dokku.me"
for CID_FILE in $DOKKU_ROOT/$TEST_APP/CONTAINER.web.*; do
assert_external_port $(< $CID_FILE) failure
done
}

View File

@@ -51,7 +51,7 @@ assert_external_port() {
assert_success assert_success
for CID_FILE in $DOKKU_ROOT/$TEST_APP/CONTAINER.web.*; do for CID_FILE in $DOKKU_ROOT/$TEST_APP/CONTAINER.web.*; do
assert_external_port $(< $CID_FILE) success assert_external_port $(< $CID_FILE) failure
done done
run dokku proxy:enable $TEST_APP run dokku proxy:enable $TEST_APP