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:
committed by
Michael Hobbs
parent
7a4464a8ae
commit
acde3d60a7
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
```
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
181
plugins/common/properties.go
Normal file
181
plugins/common/properties.go
Normal 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
|
||||||
|
}
|
||||||
@@ -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, "'"), "'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
plugins/network/.gitignore
vendored
3
plugins/network/.gitignore
vendored
@@ -1,3 +1,6 @@
|
|||||||
/commands
|
/commands
|
||||||
/subcommands/*
|
/subcommands/*
|
||||||
/network-*
|
/network-*
|
||||||
|
/install
|
||||||
|
/post-create
|
||||||
|
/post-delete
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}, "/")
|
||||||
|
|||||||
@@ -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
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
22
plugins/network/src/subcommands/set/set.go
Normal file
22
plugins/network/src/subcommands/set/set.go
Normal 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)
|
||||||
|
}
|
||||||
33
plugins/network/src/triggers/install/install.go
Normal file
33
plugins/network/src/triggers/install/install.go
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
15
plugins/network/src/triggers/post-create/post-create.go
Normal file
15
plugins/network/src/triggers/post-create/post-create.go
Normal 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")
|
||||||
|
}
|
||||||
15
plugins/network/src/triggers/post-delete/post-delete.go
Normal file
15
plugins/network/src/triggers/post-delete/post-delete.go
Normal 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)
|
||||||
|
}
|
||||||
69
tests/unit/20_network.bats
Normal file
69
tests/unit/20_network.bats
Normal 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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user