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

@@ -325,11 +325,11 @@ func VerifyAppName(appName string) (err error) {
dokkuRoot := MustGetEnv("DOKKU_ROOT")
appRoot := strings.Join([]string{dokkuRoot, appName}, "/")
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].*")
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
}

View File

@@ -538,11 +538,10 @@ dokku_deploy_cmd() {
local cmd="deploy"
source "$PLUGIN_AVAILABLE_PATH/checks/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"
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
IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG")
verify_app_name "$APP"
@@ -552,8 +551,8 @@ dokku_deploy_cmd() {
local DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE"
local oldids=$(get_app_container_ids "$APP")
local DOKKU_IS_APP_PROXY_ENABLED="$(is_app_proxy_enabled "$APP")"
local DOKKU_DOCKER_STOP_TIMEOUT="$(config_get "$APP" DOKKU_DOCKER_STOP_TIMEOUT || true)"
DOKKU_NETWORK_BIND_ALL="$(plugn trigger network-get-property "$APP" bind-all-interfaces)"
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}"
local line; local PROC_TYPE; local PROC_COUNT; local CONTAINER_INDEX
@@ -605,7 +604,7 @@ dokku_deploy_cmd() {
DOKKU_DOCKER_PORT_ARGS+=" -p $p "
done
if [[ "$DOKKU_IS_APP_PROXY_ENABLED" == "true" ]]; then
if [[ "$DOKKU_NETWORK_BIND_ALL" == "false" ]]; then
# shellcheck disable=SC2086
cid=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d -e PORT=$DOKKU_PORT $DOCKER_ARGS $IMAGE $START_CMD)
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) {
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) {
if os.Getenv("DOKKU_QUIET_OUTPUT") != "" {
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
}