diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index 365cd3193f9..dd66f2196cb 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -8,6 +8,7 @@ - Users no longer need to clear the `source-image` git property when transitioning from image-based deploys (`git:from-image` and `git:load-image`) to other deployment methods (git push, `git:from-archive`, `git:sync`). - For deploys via the `git:from-image` and `git:load-image` commands, the `CHECKS` file is now extracted from the configured `WORKDIR` property of the image. For all other deploys - git push, `git:from-archive`, `git:sync` - will have the `CHECKS` extracted directly from the source code. The filename in both cases is `CHECKS` and cannot be modified. - The `common#get_app_raw_tcp_ports()` function has been deprecated and will be removed in the next release. Users should avoid interacting with this function for dockerfile ports and instead use the `ports-get` plugin trigger for fetching ports for an app. +- The `common#get_available_port()` function has been deprecated and will be removed in the next release. Users should avoid interacting with this function and instead use the `ports-get-available` plugin trigger for fetching an available port. ## Deprecations diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 8fd94e8f5d9..f1fc0c5f6ce 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1288,6 +1288,21 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # TODO ``` +### `ports-get-available` + +- Description: Prints out an available port greater than 1024 +- Invoked by: Various networking plugins +- Arguments `$APP` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +# TODO +``` + ### `post-app-clone` - Description: Allows you to run commands after an app was cloned. diff --git a/plugins/20_events/ports-get-available b/plugins/20_events/ports-get-available new file mode 120000 index 00000000000..5178a749ff6 --- /dev/null +++ b/plugins/20_events/ports-get-available @@ -0,0 +1 @@ +hook \ No newline at end of file diff --git a/plugins/common/functions b/plugins/common/functions index 23043bbd556..5357269adf5 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -699,15 +699,8 @@ docker_cleanup() { get_available_port() { declare desc="returns first currently unused port > 1024" - while true; do - local port=$(shuf -i 1025-65535 -n 1) - if ! nc -z 0.0.0.0 "$port"; then - echo "$port" - return 0 - else - continue - fi - done + dokku_log_warn "Deprecated: please use the 'ports-get-available' plugin trigger instead" + plugn trigger ports-get-available } dokku_auth() { diff --git a/plugins/ports/Makefile b/plugins/ports/Makefile index 8a4da72d25d..962d514703e 100644 --- a/plugins/ports/Makefile +++ b/plugins/ports/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/list subcommands/add subcommands/clear subcommands/remove subcommands/set subcommands/report -TRIGGERS = triggers/ports-clear triggers/ports-dockerfile-raw-tcp-ports triggers/ports-get triggers/post-certs-remove triggers/post-certs-update triggers/report +TRIGGERS = triggers/ports-clear triggers/ports-dockerfile-raw-tcp-ports triggers/ports-get triggers/ports-get-available triggers/post-certs-remove triggers/post-certs-update triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = ports diff --git a/plugins/ports/functions.go b/plugins/ports/functions.go index 0f701a664c0..05c087d7377 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -3,6 +3,7 @@ package ports import ( "errors" "fmt" + "net" "os" "sort" "strconv" @@ -38,6 +39,26 @@ func filterAppPortMaps(appName string, scheme string, hostPort int) []PortMap { return filteredPortMaps } +func getAvailablePort() int { + addr, err := net.ResolveTCPAddr("tcp", "localhost:0") + if err != nil { + return 0 + } + + for { + l, err := net.ListenTCP("tcp", addr) + if err != nil { + return 0 + } + defer l.Close() + + port := l.Addr().(*net.TCPAddr).Port + if port >= 1025 && port <= 65535 { + return port + } + } +} + func getDockerfileRawTCPPorts(appName string) []int { b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_DOCKERFILE_PORTS"}...) dockerfilePorts := strings.TrimSpace(string(b[:])) diff --git a/plugins/ports/proxy-configure-ports b/plugins/ports/proxy-configure-ports index deee9b39da9..4f56e11db13 100755 --- a/plugins/ports/proxy-configure-ports +++ b/plugins/ports/proxy-configure-ports @@ -8,7 +8,7 @@ trigger-ports-proxy-configure-ports() { declare desc="ports proxy-configure-ports plugin trigger" declare trigger="proxy-configure-ports" declare APP="$1" - local RAW_TCP_PORTS="$(get_app_raw_tcp_ports "$APP")" + local RAW_TCP_PORTS="$(plugn trigger ports-dockerfile-raw-tcp-ports "$APP" | xargs)" local DOKKU_PROXY_PORT=$(config_get "$APP" DOKKU_PROXY_PORT) local DOKKU_PROXY_SSL_PORT=$(config_get "$APP" DOKKU_PROXY_SSL_PORT) local DOKKU_PROXY_PORT_MAP=$(config_get "$APP" DOKKU_PROXY_PORT_MAP) @@ -20,7 +20,7 @@ trigger-ports-proxy-configure-ports() { if [[ -z "$DOKKU_PROXY_PORT" ]] && [[ -z "$RAW_TCP_PORTS" ]]; then if [[ "$IS_APP_VHOST_ENABLED" == "false" ]]; then dokku_log_info1 "No port set, setting to random open high port" - local PROXY_PORT=$(get_available_port) + local PROXY_PORT=$(plugn trigger ports-get-available) else local PROXY_PORT=$(config_get --global DOKKU_PROXY_PORT) PROXY_PORT=${PROXY_PORT:=80} @@ -33,7 +33,7 @@ trigger-ports-proxy-configure-ports() { PROXY_SSL_PORT=${PROXY_SSL_PORT:=443} if [[ -z "$RAW_TCP_PORTS" ]] && [[ "$IS_APP_VHOST_ENABLED" == "false" ]]; then dokku_log_info1 "No ssl port set, setting to random open high port" - PROXY_SSL_PORT=$(get_available_port) + PROXY_SSL_PORT=$(plugn trigger ports-get-available) fi DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_PROXY_SSL_PORT="$PROXY_SSL_PORT" fi diff --git a/plugins/ports/src/triggers/triggers.go b/plugins/ports/src/triggers/triggers.go index 75d40616ddc..bb125943c04 100644 --- a/plugins/ports/src/triggers/triggers.go +++ b/plugins/ports/src/triggers/triggers.go @@ -18,15 +18,17 @@ func main() { var err error switch trigger { - case "ports-get": - appName := flag.Arg(0) - err = ports.TriggerPortsGet(appName) case "ports-clear": appName := flag.Arg(0) err = ports.TriggerPortsClear(appName) case "ports-dockerfile-raw-tcp-ports": appName := flag.Arg(0) err = ports.TriggerPortsDockerfileRawTCPPorts(appName) + case "ports-get": + appName := flag.Arg(0) + err = ports.TriggerPortsGet(appName) + case "ports-get-available": + err = ports.TriggerPortsGetAvailable() case "post-certs-remove": appName := flag.Arg(0) err = ports.TriggerPostCertsRemove(appName) diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index 39aa0b49a72..ea842f83a45 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -35,6 +35,16 @@ func TriggerPortsGet(appName string) error { return nil } +// TriggerPortsGetAvailable prints out an available port greater than 1024 +func TriggerPortsGetAvailable() error { + port := getAvailablePort() + if port > 0 { + common.Log(fmt.Sprint(port)) + } + + return nil +} + // TriggerPostCertsRemove unsets port config vars after SSL cert is added func TriggerPostCertsRemove(appName string) error { keys := []string{"DOKKU_PROXY_SSL_PORT"}