feat: route paths to non-web processes via proxy:route
Adds proxy:route:set/remove/clear/report so users can declare path-prefix routes to non-web Procfile processes without hand-writing nginx.conf.sigil. Routes are stored on the proxy plugin as a PropertyMap of path to process:port:strip, with set semantics so each invocation produces a fully-determined state and re-application is idempotent. A proxy-supports-routes capability trigger gates the command per backend - nginx-vhosts, traefik-vhosts, caddy-vhosts, and scheduler-k3s render routes today; openresty-vhosts and haproxy-vhosts refuse until their sidecar images learn a path-prefix vocabulary (tracked in dokku/openresty-docker-proxy#137 and the haproxy sidecar). Closes #8725.
This commit is contained in:
@@ -14,11 +14,26 @@ trigger-traefik-vhosts-docker-args-process-deploy() {
|
||||
local proxy_container_https_port proxy_container_https_port_candidate proxy_host_https_port_candidate
|
||||
local STDIN=$(cat)
|
||||
|
||||
if [[ "$PROC_TYPE" != "web" ]]; then
|
||||
if [[ "$(plugn trigger proxy-type "$APP")" != "traefik" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$(plugn trigger proxy-type "$APP")" != "traefik" ]]; then
|
||||
if [[ "$PROC_TYPE" != "web" ]]; then
|
||||
# Non-web processes are only labeled when at least one path-based route
|
||||
# targets the process. Otherwise traefik should ignore the container.
|
||||
local route_line route_proc
|
||||
local has_route=false
|
||||
while IFS='|' read -r route_proc _ _ _; do
|
||||
if [[ "$route_proc" == "$PROC_TYPE" ]]; then
|
||||
has_route=true
|
||||
break
|
||||
fi
|
||||
done < <(plugn trigger proxy-routes-list "$APP")
|
||||
if [[ "$has_route" != "true" ]]; then
|
||||
echo -n "$STDIN"
|
||||
return
|
||||
fi
|
||||
fn-traefik-route-labels "$APP" "$PROC_TYPE" "$STDIN"
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
@@ -4,6 +4,87 @@ source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions"
|
||||
set -eo pipefail
|
||||
[[ $DOKKU_TRACE ]] && set -x
|
||||
|
||||
fn-traefik-route-labels() {
|
||||
declare desc="emits per-route traefik labels for a non-web container"
|
||||
declare APP="$1" PROC_TYPE="$2" STDIN="$3"
|
||||
local host_rule output route_proc route_port route_path route_strip
|
||||
local router_name service_name middleware_name path_len
|
||||
|
||||
host_rule="$(fn-traefik-host-rule "$APP")"
|
||||
output="--label traefik.enable=true"
|
||||
|
||||
while IFS='|' read -r route_proc route_port route_path route_strip; do
|
||||
[[ -z "$route_proc" ]] && continue
|
||||
[[ "$route_proc" != "$PROC_TYPE" ]] && continue
|
||||
|
||||
local route_slug
|
||||
route_slug="$(fn-traefik-route-slug "$route_path")"
|
||||
router_name="${APP}-${route_proc}-${route_slug}"
|
||||
service_name="${APP}-${route_proc}-${route_slug}"
|
||||
path_len="${#route_path}"
|
||||
|
||||
local rule="PathPrefix(\`${route_path}\`)"
|
||||
if [[ -n "$host_rule" ]]; then
|
||||
rule="(${host_rule}) && ${rule}"
|
||||
fi
|
||||
|
||||
output="$output --label \"traefik.http.routers.${router_name}.rule=${rule}\""
|
||||
output="$output --label traefik.http.routers.${router_name}.entrypoints=$(fn-traefik-computed-http-entry-point)"
|
||||
output="$output --label traefik.http.routers.${router_name}.priority=${path_len}"
|
||||
output="$output --label traefik.http.routers.${router_name}.service=${service_name}"
|
||||
output="$output --label traefik.http.services.${service_name}.loadbalancer.server.port=${route_port}"
|
||||
|
||||
if [[ "$route_strip" == "1" ]]; then
|
||||
middleware_name="${router_name}-strip"
|
||||
output="$output --label traefik.http.middlewares.${middleware_name}.stripprefix.prefixes=${route_path}"
|
||||
output="$output --label traefik.http.routers.${router_name}.middlewares=${middleware_name}"
|
||||
fi
|
||||
done < <(plugn trigger proxy-routes-list "$APP")
|
||||
|
||||
local proxy_labels_file_path=$(fn-proxy-get-labels-file-path "traefik" "$APP")
|
||||
if [[ -f "$proxy_labels_file_path" ]]; then
|
||||
while read -r line; do
|
||||
[[ -z "$line" ]] && continue
|
||||
output="$output --label '$line'"
|
||||
done <"$proxy_labels_file_path"
|
||||
fi
|
||||
|
||||
echo -n "$STDIN $output"
|
||||
}
|
||||
|
||||
fn-traefik-route-slug() {
|
||||
declare desc="returns a deterministic kebab-case slug for a route path"
|
||||
declare ROUTE_PATH="$1"
|
||||
local s="${ROUTE_PATH#/}"
|
||||
s="${s%/}"
|
||||
s="${s//\//-}"
|
||||
s="${s,,}"
|
||||
if [[ -z "$s" ]]; then
|
||||
s="root"
|
||||
fi
|
||||
echo "$s"
|
||||
}
|
||||
|
||||
fn-traefik-host-rule() {
|
||||
declare desc="builds a traefik Host(\`d1\`) || Host(\`d2\`) rule fragment from an app's domains"
|
||||
declare APP="$1"
|
||||
local app_domains domain result=""
|
||||
|
||||
app_domains="$(plugn trigger domains-list "$APP")"
|
||||
if [[ -z "$app_domains" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
for domain in $(echo "$app_domains" | xargs); do
|
||||
if [[ -z "$result" ]]; then
|
||||
result="Host(\`${domain}\`)"
|
||||
continue
|
||||
fi
|
||||
result="${result} || Host(\`${domain}\`)"
|
||||
done
|
||||
echo "$result"
|
||||
}
|
||||
|
||||
fn-traefik-logs() {
|
||||
declare desc="shows the logs for the traefik container"
|
||||
declare TAIL="$1" NUM="$2"
|
||||
|
||||
18
plugins/traefik-vhosts/proxy-supports-routes
Executable file
18
plugins/traefik-vhosts/proxy-supports-routes
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
[[ $DOKKU_TRACE ]] && set -x
|
||||
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
|
||||
|
||||
trigger-traefik-vhosts-proxy-supports-routes() {
|
||||
declare desc="advertises path-based routing support for traefik"
|
||||
declare trigger="proxy-supports-routes"
|
||||
declare APP="$1"
|
||||
|
||||
if [[ "$(plugn trigger proxy-type "$APP")" != "traefik" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
echo "true"
|
||||
}
|
||||
|
||||
trigger-traefik-vhosts-proxy-supports-routes "$@"
|
||||
Reference in New Issue
Block a user