diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index cd0dabd1e4c..041bcfb55c6 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -9,6 +9,8 @@ ## Deprecations - The `proxy:ports*` commands have been replaced with the new `ports` plugin. Users will be able to use the old `proxy:ports*` commands for a single minor release, and they will be removed in the next minor release. +- 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 `proxy-configure-ports` plugin trigger has been deprecated and will be removed in the next release. Users should instead trigger the `ports-configure` plugin trigger. ## Un-Deprecations diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 91e8887c6d9..692804f5bfc 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1226,6 +1226,38 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x nginx -t ``` +### `ports-configure` + +- Description: Configures the port mapping +- Invoked by: `internally triggered by proxy plugins` +- Arguments: `$APP` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +# TODO +``` + +### `ports-dockerfile-raw-tcp-ports` + +> Warning: This trigger is for internal use and will be removed in a future release. Do not use it in your codebase. + +- Description: Extracts raw tcp port numbers from DOCKERFILE_PORTS config variable +- Invoked by: `internally triggered by proxy 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. @@ -1857,6 +1889,8 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x ### `proxy-configure-ports` +> Warning: Deprecated, please use `ports-configure` instead + - Description: Configures the port mapping - Invoked by: `internally triggered by proxy plugins` - Arguments: `$APP` diff --git a/plugins/20_events/ports-configure b/plugins/20_events/ports-configure new file mode 120000 index 00000000000..5178a749ff6 --- /dev/null +++ b/plugins/20_events/ports-configure @@ -0,0 +1 @@ +hook \ No newline at end of file diff --git a/plugins/20_events/ports-dockerfile-raw-tcp-ports b/plugins/20_events/ports-dockerfile-raw-tcp-ports new file mode 120000 index 00000000000..5178a749ff6 --- /dev/null +++ b/plugins/20_events/ports-dockerfile-raw-tcp-ports @@ -0,0 +1 @@ +hook \ No newline at end of file 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/caddy-vhosts/docker-args-process-deploy b/plugins/caddy-vhosts/docker-args-process-deploy index 4fe047894c3..e7690d5d2ac 100755 --- a/plugins/caddy-vhosts/docker-args-process-deploy +++ b/plugins/caddy-vhosts/docker-args-process-deploy @@ -30,7 +30,7 @@ trigger-caddy-vhosts-docker-args-process-deploy() { fi # ensure we have a port mapping - plugn trigger proxy-configure-ports "$APP" + plugn trigger ports-configure "$APP" # gather port mapping information # we only support proxying a single port for http and https listeners diff --git a/plugins/common/functions b/plugins/common/functions index f1f49bc8378..ccbde18e64a 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -815,18 +815,10 @@ extract_directive_from_dockerfile() { get_app_raw_tcp_ports() { declare desc="extracts raw tcp port numbers from DOCKERFILE_PORTS config variable" - source "$PLUGIN_AVAILABLE_PATH/config/functions" + declare APP="$1" - local APP="$1" - local DOCKERFILE_PORTS="$(config_get "$APP" DOKKU_DOCKERFILE_PORTS)" - for p in $DOCKERFILE_PORTS; do - if [[ ! "$p" =~ .*udp.* ]]; then - p=${p//\/tcp/} - raw_tcp_ports+="$p " - fi - done - local raw_tcp_ports="$(echo "$raw_tcp_ports" | xargs)" - echo "$raw_tcp_ports" + dokku_log_warn "Deprecated: please use the 'ports-dockerfile-raw-tcp-ports' plugin trigger instead" + plugn trigger ports-dockerfile-raw-tcp-ports "$APP" | xargs } get_container_ports() { diff --git a/plugins/common/ports-get-available b/plugins/common/ports-get-available new file mode 100755 index 00000000000..f7d9893334a --- /dev/null +++ b/plugins/common/ports-get-available @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +get_available_port "$@" diff --git a/plugins/domains/internal-functions b/plugins/domains/internal-functions index 921ac9885bb..e4b13e5918e 100755 --- a/plugins/domains/internal-functions +++ b/plugins/domains/internal-functions @@ -137,7 +137,7 @@ fn-domains-generate-urls() { fn-domains-generate-urls-from-config() { declare APP="$1" SCHEME="$2" VHOST="$3" DEFAULT_LISTEN_PORT="$4" local DOKKU_PROXY_PORT_MAP=$(plugn trigger config-get "$APP" DOKKU_PROXY_PORT_MAP || true) - local RAW_TCP_PORTS="$(get_app_raw_tcp_ports "$APP")" + local RAW_TCP_PORTS="$(plugn trigger ports-dockerfile-raw-tcp-ports "$APP" | xargs)" if [[ "$(plugn trigger proxy-is-enabled "$APP")" == "false" ]]; then if [[ -n "$RAW_TCP_PORTS" ]]; then diff --git a/plugins/haproxy-vhosts/docker-args-process-deploy b/plugins/haproxy-vhosts/docker-args-process-deploy index f08713a5e79..7a3da3c585e 100755 --- a/plugins/haproxy-vhosts/docker-args-process-deploy +++ b/plugins/haproxy-vhosts/docker-args-process-deploy @@ -33,7 +33,7 @@ trigger-haproxy-vhosts-docker-args-process-deploy() { plugn trigger domains-setup "$APP" >/dev/null # ensure we have a port mapping - plugn trigger proxy-configure-ports "$APP" + plugn trigger ports-configure "$APP" # gather port mapping information # we only support proxying a single port for http and https listeners diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions index 1e3d2f1e27a..6c3a3914860 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -294,7 +294,7 @@ nginx_build_config() { local SCHEME=http local NGINX_TEMPLATE_SOURCE="built-in" local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls" - 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_APP_LISTENERS CUSTOM_NGINX_TEMPLATE="$(plugn trigger nginx-app-template-source "$APP" "app-config")" @@ -318,7 +318,7 @@ nginx_build_config() { fi # setup nginx listen ports - plugn trigger proxy-configure-ports "$APP" + plugn trigger ports-configure "$APP" local PROXY_PORT=$(config_get "$APP" DOKKU_PROXY_PORT) local PROXY_SSL_PORT=$(config_get "$APP" DOKKU_PROXY_SSL_PORT) local PROXY_PORT_MAP=$(config_get "$APP" DOKKU_PROXY_PORT_MAP) diff --git a/plugins/ports/.gitignore b/plugins/ports/.gitignore index 291036bd409..3fc2c0d2f86 100644 --- a/plugins/ports/.gitignore +++ b/plugins/ports/.gitignore @@ -3,5 +3,6 @@ /triggers/* /triggers /install +/ports-* /post-* /report diff --git a/plugins/ports/Makefile b/plugins/ports/Makefile index 2149ff8543e..5eccbd3a0bf 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/post-certs-remove triggers/post-certs-update triggers/report +TRIGGERS = triggers/ports-configure triggers/ports-dockerfile-raw-tcp-ports 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 06fdc164115..78d6be532d1 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -31,6 +31,28 @@ func filterAppPortMaps(appName string, scheme string, hostPort int) []PortMap { return filteredPortMaps } +func getDockerfileRawTCPPorts(appName string) []string { + b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_DOCKERFILE_PORTS"}...) + dockerfilePorts := strings.TrimSpace(string(b[:])) + + ports := []string{} + for _, port := range strings.Split(dockerfilePorts, " ") { + port = strings.TrimSpace(port) + if strings.HasSuffix(port, "/udp") { + continue + } + + port = strings.TrimSuffix(port, "/tcp") + if port == "" { + continue + } + + ports = append(ports, port) + } + + return ports +} + func getPortMaps(appName string) []PortMap { value := config.GetWithDefault(appName, "DOKKU_PROXY_PORT_MAP", "") portMaps, _ := parsePortMapString(value) diff --git a/plugins/ports/proxy-configure-ports b/plugins/ports/proxy-configure-ports index deee9b39da9..64910658803 100755 --- a/plugins/ports/proxy-configure-ports +++ b/plugins/ports/proxy-configure-ports @@ -1,6 +1,5 @@ #!/usr/bin/env bash source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_AVAILABLE_PATH/config/functions" set -eo pipefail [[ $DOKKU_TRACE ]] && set -x @@ -8,58 +7,9 @@ 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 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) - local IS_APP_VHOST_ENABLED=true - local UPSTREAM_PORT="5000" - plugn trigger domains-vhost-enabled "$APP" 2>/dev/null || IS_APP_VHOST_ENABLED=false - - 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) - else - local PROXY_PORT=$(config_get --global DOKKU_PROXY_PORT) - PROXY_PORT=${PROXY_PORT:=80} - fi - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_PROXY_PORT="$PROXY_PORT" - fi - if [[ -z "$DOKKU_PROXY_SSL_PORT" ]]; then - if [[ "$(plugn trigger certs-exists "$APP")" == "true" ]]; then - local PROXY_SSL_PORT=$(config_get --global DOKKU_PROXY_SSL_PORT) - 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) - fi - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_PROXY_SSL_PORT="$PROXY_SSL_PORT" - fi - fi - if [[ -z "$DOKKU_PROXY_PORT_MAP" ]]; then - if [[ -n "$RAW_TCP_PORTS" ]]; then - local RAW_TCP_PORT - for RAW_TCP_PORT in $RAW_TCP_PORTS; do - local PROXY_PORT_MAP+=" http:${RAW_TCP_PORT}:${RAW_TCP_PORT} " - done - else - local PROXY_PORT=${PROXY_PORT:-$DOKKU_PROXY_PORT} - local PROXY_SSL_PORT=${PROXY_SSL_PORT:-$DOKKU_PROXY_SSL_PORT} - [[ -f "$DOKKU_ROOT/$APP/PORT.web.1" ]] && local UPSTREAM_PORT="$(<"$DOKKU_ROOT/$APP/PORT.web.1")" - if [[ -n "$PROXY_PORT" ]] && [[ -n "$PROXY_SSL_PORT" ]]; then - local PROXY_PORT_MAP+=" http:${PROXY_PORT}:$UPSTREAM_PORT https:${PROXY_SSL_PORT}:$UPSTREAM_PORT " - elif [[ -n "$PROXY_PORT" ]]; then - local PROXY_PORT_MAP+=" http:${PROXY_PORT}:$UPSTREAM_PORT " - fi - fi - if [[ -n "$PROXY_PORT_MAP" ]]; then - local PROXY_PORT_MAP="$(echo "$PROXY_PORT_MAP" | xargs)" - local PROXY_PORT_MAP+=" $(merge_dedupe_list "$(remove_val_from_list "$PORT_MAP" "$DOKKU_PROXY_PORT_MAP" " ")" " ") " - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_PROXY_PORT_MAP="$PROXY_PORT_MAP" - fi - fi + dokku_log_warn "Deprecated: please use the 'ports-configure' plugin trigger instead" + plugn trigger ports-configure "$APP" } trigger-ports-proxy-configure-ports "$@" diff --git a/plugins/ports/src/triggers/triggers.go b/plugins/ports/src/triggers/triggers.go index f159dbf57d2..b7e11ab21fa 100644 --- a/plugins/ports/src/triggers/triggers.go +++ b/plugins/ports/src/triggers/triggers.go @@ -18,6 +18,12 @@ func main() { var err error switch trigger { + case "ports-configure": + appName := flag.Arg(0) + err = ports.TriggerPortsConfigure(appName) + case "ports-dockerfile-raw-tcp-ports": + appName := flag.Arg(0) + err = ports.TriggerPortsDockerfileRawTCPPorts(appName) 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 aa209200a7b..a5033e09a06 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -1,9 +1,144 @@ package ports import ( + "fmt" + "path/filepath" + "strings" + + "github.com/dokku/dokku/plugins/common" "github.com/dokku/dokku/plugins/config" ) +// TriggerRawTCPPorts extracts raw tcp port numbers from DOCKERFILE_PORTS config variable +func TriggerPortsDockerfileRawTCPPorts(appName string) error { + ports := getDockerfileRawTCPPorts(appName) + for _, port := range ports { + common.Log(port) + } + + return nil +} + +// TriggerPortsConfigure ensures we have a port mapping +func TriggerPortsConfigure(appName string) error { + rawTCPPorts := getDockerfileRawTCPPorts(appName) + + b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_PROXY_PORT"}...) + dokkuProxyPort := strings.TrimSpace(string(b[:])) + + b, _ = common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_PROXY_SSL_PORT"}...) + dokkuProxySSLPort := strings.TrimSpace(string(b[:])) + + b, _ = common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_PROXY_PORT_MAP"}...) + portMapString := strings.TrimSpace(string(b[:])) + + isAppVhostEnabled := true + upstreamPort := "5000" + + if err := common.PlugnTrigger("domains-vhost-enabled", []string{appName}...); err != nil { + isAppVhostEnabled = false + } + + if dokkuProxyPort == "" && len(rawTCPPorts) == 0 { + proxyPort := "80" + if !isAppVhostEnabled { + common.LogInfo1("No port set, setting to random open high port") + b, _ = common.PlugnTriggerOutput("ports-get-available", []string{}...) + proxyPort = strings.TrimSpace(string(b[:])) + } else { + b, _ = common.PlugnTriggerOutput("config-get-global", []string{"DOKKU_PROXY_PORT"}...) + proxyPort = strings.TrimSpace(string(b[:])) + } + + if proxyPort == "" { + proxyPort = "80" + } + + dokkuProxyPort = proxyPort + err := common.EnvWrap(func() error { + entries := map[string]string{ + "DOKKU_PROXY_PORT": proxyPort, + } + return config.SetMany(appName, entries, false) + }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) + if err != nil { + return err + } + } + + if dokkuProxySSLPort == "" { + b, _ = common.PlugnTriggerOutput("certs-exists", []string{appName}...) + certsExists := strings.TrimSpace(string(b[:])) + if certsExists == "true" { + b, _ = common.PlugnTriggerOutput("config-get-global", []string{"PROXY_SSL_PORT"}...) + proxySSLPort := strings.TrimSpace(string(b[:])) + if proxySSLPort == "" { + proxySSLPort = "443" + } + + if len(rawTCPPorts) == 0 && !isAppVhostEnabled { + common.LogInfo1("No ssl port set, setting to random open high port") + b, _ = common.PlugnTriggerOutput("ports-get-available", []string{}...) + proxySSLPort = strings.TrimSpace(string(b[:])) + } + + dokkuProxySSLPort = proxySSLPort + err := common.EnvWrap(func() error { + entries := map[string]string{ + "DOKKU_PROXY_SSL_PORT": proxySSLPort, + } + return config.SetMany(appName, entries, false) + }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) + if err != nil { + return err + } + } + } + + if len(portMapString) == 0 { + if len(rawTCPPorts) > 0 { + for _, rawTcpPort := range rawTCPPorts { + if rawTcpPort == "" { + continue + } + + portMapString = fmt.Sprintf("%s http:%s:%s", portMapString, rawTcpPort, rawTcpPort) + } + } else { + portFile := filepath.Join(common.AppRoot(appName), "PORT.web.1") + if common.FileExists(portFile) { + upstreamPort = common.ReadFirstLine(portFile) + } + + if dokkuProxyPort != "" { + portMapString = fmt.Sprintf("%s http:%s:%s", portMapString, dokkuProxyPort, upstreamPort) + } + if dokkuProxySSLPort != "" { + portMapString = fmt.Sprintf("%s https:%s:%s", portMapString, dokkuProxySSLPort, upstreamPort) + } + } + + portMapString = strings.TrimSpace(portMapString) + if len(portMapString) > 0 { + portMaps, err := parsePortMapString(portMapString) + if err != nil { + return err + } + + err = common.EnvWrap(func() error { + return setPortMaps(appName, portMaps) + }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) + if err != nil { + return err + } + } + + return nil + } + + return nil +} + // TriggerPostCertsRemove unsets port config vars after SSL cert is added func TriggerPostCertsRemove(appName string) error { keys := []string{"DOKKU_PROXY_SSL_PORT"} diff --git a/plugins/traefik-vhosts/docker-args-process-deploy b/plugins/traefik-vhosts/docker-args-process-deploy index c81248ceaf1..b9960ec280a 100755 --- a/plugins/traefik-vhosts/docker-args-process-deploy +++ b/plugins/traefik-vhosts/docker-args-process-deploy @@ -30,7 +30,7 @@ trigger-traefik-vhosts-docker-args-process-deploy() { fi # ensure we have a port mapping - plugn trigger proxy-configure-ports "$APP" + plugn trigger ports-configure "$APP" # gather port mapping information # we only support proxying a single port for http and https listeners