diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index dd66f2196cb..b07d22ca8a1 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -7,12 +7,13 @@ - The `traefik` integration now mounts config to `/data` instead of the path `/acme.json`, fixing permissions issues under certain architectures. To take advantage of the new functionality, the traefik container should be stopped (via `dokku traefik:stop`) and then started (via `dokku traefik:start`). - 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 - 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 `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. +- 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 f1fc0c5f6ce..a3ddf285f53 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1256,6 +1256,21 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # TODO ``` +### `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. @@ -1934,6 +1949,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/caddy-vhosts/docker-args-process-deploy b/plugins/caddy-vhosts/docker-args-process-deploy index 1d1fdd28ede..65a9c97963c 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/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/haproxy-vhosts/docker-args-process-deploy b/plugins/haproxy-vhosts/docker-args-process-deploy index 9546d7280c6..321b403fe23 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 687a09f6bd7..9f65f1be207 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -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=$(plugn trigger ports-get "$APP") diff --git a/plugins/ports/Makefile b/plugins/ports/Makefile index 962d514703e..189f7b8991b 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/ports-get-available triggers/post-certs-remove triggers/post-certs-update triggers/report +TRIGGERS = triggers/ports-clear triggers/ports-configure 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 05c087d7377..d01d5dcbbaa 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -28,6 +28,12 @@ func clearPorts(appName string) error { }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) } +func doesCertExist(appName string) bool { + b, _ := common.PlugnTriggerOutput("certs-exists", []string{appName}...) + certsExists := strings.TrimSpace(string(b[:])) + return certsExists == "true" +} + func filterAppPortMaps(appName string, scheme string, hostPort int) []PortMap { var filteredPortMaps []PortMap for _, portMap := range getPortMaps(appName) { @@ -90,12 +96,59 @@ func getDockerfileRawTCPPorts(appName string) []int { return ports } +func getGlobalProxyPort() int { + port := 0 + b, _ := common.PlugnTriggerOutput("config-get-global", []string{"DOKKU_PROXY_PORT"}...) + if intVar, err := strconv.Atoi(strings.TrimSpace(string(b[:]))); err == nil { + port = intVar + } + + return port +} + +func getGlobalProxySSLPort() int { + port := 0 + b, _ := common.PlugnTriggerOutput("config-get-global", []string{"DOKKU_PROXY_SSL_PORT"}...) + if intVar, err := strconv.Atoi(strings.TrimSpace(string(b[:]))); err == nil { + port = intVar + } + + return port +} + func getPortMaps(appName string) []PortMap { value := config.GetWithDefault(appName, "DOKKU_PROXY_PORT_MAP", "") portMaps, _ := parsePortMapString(value) return portMaps } +func getProxyPort(appName string) int { + port := 0 + b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_PROXY_PORT"}...) + if intVar, err := strconv.Atoi(strings.TrimSpace(string(b[:]))); err == nil { + port = intVar + } + + return port +} + +func getProxySSLPort(appName string) int { + port := 0 + b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_PROXY_SSL_PORT"}...) + if intVar, err := strconv.Atoi(strings.TrimSpace(string(b[:]))); err == nil { + port = intVar + } + + return port +} + +func isAppVhostEnabled(appName string) bool { + if err := common.PlugnTrigger("domains-vhost-enabled", []string{appName}...); err != nil { + return false + } + return true +} + func inRange(value int, min int, max int) bool { return min < value && value < max } @@ -223,11 +276,13 @@ func setPortMaps(appName string, portMaps []PortMap) error { value = append(value, portMap.String()) } - sort.Strings(value) - entries := map[string]string{ - "DOKKU_PROXY_PORT_MAP": strings.Join(value, " "), - } - return config.SetMany(appName, entries, false) + return common.EnvWrap(func() error { + sort.Strings(value) + entries := map[string]string{ + "DOKKU_PROXY_PORT_MAP": strings.Join(value, " "), + } + return config.SetMany(appName, entries, false) + }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) } func uniquePortMaps(portMaps []PortMap) []PortMap { diff --git a/plugins/ports/proxy-configure-ports b/plugins/ports/proxy-configure-ports index 4f56e11db13..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="$(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) - 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=$(plugn trigger ports-get-available) - 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=$(plugn trigger ports-get-available) - 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 bb125943c04..6551298da45 100644 --- a/plugins/ports/src/triggers/triggers.go +++ b/plugins/ports/src/triggers/triggers.go @@ -21,6 +21,9 @@ func main() { case "ports-clear": appName := flag.Arg(0) err = ports.TriggerPortsClear(appName) + 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) diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index ea842f83a45..9da826bf20a 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -2,6 +2,8 @@ package ports import ( "fmt" + "path/filepath" + "strconv" "github.com/dokku/dokku/plugins/common" "github.com/dokku/dokku/plugins/config" @@ -12,6 +14,110 @@ func TriggerPortsClear(appName string) error { return clearPorts(appName) } +// TriggerPortsConfigure ensures we have a port mapping +func TriggerPortsConfigure(appName string) error { + rawTCPPorts := getDockerfileRawTCPPorts(appName) + + dokkuProxyPort := getProxyPort(appName) + dokkuProxySSLPort := getProxySSLPort(appName) + portMaps := getPortMaps(appName) + + vhostEnabled := isAppVhostEnabled(appName) + + if dokkuProxyPort == 0 && len(rawTCPPorts) == 0 { + proxyPort := 80 + if !vhostEnabled { + common.LogInfo1("No port set, setting to random open high port") + proxyPort = getAvailablePort() + } else { + proxyPort = getGlobalProxyPort() + } + + if proxyPort == 0 { + proxyPort = 80 + } + + dokkuProxyPort = proxyPort + err := common.EnvWrap(func() error { + entries := map[string]string{ + "DOKKU_PROXY_PORT": fmt.Sprint(proxyPort), + } + return config.SetMany(appName, entries, false) + }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) + if err != nil { + return err + } + } + + if dokkuProxySSLPort == 0 { + if doesCertExist(appName) { + proxySSLPort := getGlobalProxySSLPort() + if proxySSLPort == 0 { + proxySSLPort = 443 + } + + if len(rawTCPPorts) == 0 && !vhostEnabled { + common.LogInfo1("No ssl port set, setting to random open high port") + proxySSLPort = getAvailablePort() + } + + dokkuProxySSLPort = proxySSLPort + err := common.EnvWrap(func() error { + entries := map[string]string{ + "DOKKU_PROXY_SSL_PORT": fmt.Sprint(proxySSLPort), + } + return config.SetMany(appName, entries, false) + }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) + if err != nil { + return err + } + } + } + + if len(portMaps) == 0 { + if len(rawTCPPorts) > 0 { + for _, rawTcpPort := range rawTCPPorts { + portMaps = append(portMaps, PortMap{ + ContainerPort: rawTcpPort, + HostPort: rawTcpPort, + Scheme: "http", + }) + } + } else { + upstreamPort := 5000 + portFile := filepath.Join(common.AppRoot(appName), "PORT.web.1") + if common.FileExists(portFile) { + if port, err := strconv.Atoi(common.ReadFirstLine(portFile)); err == nil { + upstreamPort = port + } + } + + if dokkuProxyPort != 0 { + portMaps = append(portMaps, PortMap{ + ContainerPort: upstreamPort, + HostPort: dokkuProxyPort, + Scheme: "http", + }) + } + if dokkuProxySSLPort != 0 { + portMaps = append(portMaps, PortMap{ + ContainerPort: upstreamPort, + HostPort: dokkuProxySSLPort, + Scheme: "https", + }) + } + } + + if len(portMaps) > 0 { + return setPortMaps(appName, portMaps) + } + + return nil + } + + return nil +} + // TriggerRawTCPPorts extracts raw tcp port numbers from DOCKERFILE_PORTS config variable func TriggerPortsDockerfileRawTCPPorts(appName string) error { ports := getDockerfileRawTCPPorts(appName) diff --git a/plugins/traefik-vhosts/docker-args-process-deploy b/plugins/traefik-vhosts/docker-args-process-deploy index 4f09b45cdb3..f9d94ffa46b 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