feat: port vhost proxy :report subcommands to golang

The bash :report implementations for the caddy, haproxy, openresty, and traefik proxy plugins are replaced with a compiled golang binary that collects report keys in parallel and marshals json directly. The traefik dns-provider values keep their masking behaviour, remaining hidden in the default stdout report while surfacing for --format json or an explicit flag query. These four plugins previously had no unit tests, so a bats suite covering the report matrix is added for each.
This commit is contained in:
Jose Diaz-Gonzalez
2026-07-07 06:29:01 -04:00
parent f9db9583a1
commit 5faabea3d4
49 changed files with 1853 additions and 605 deletions

4
plugins/traefik-vhosts/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
/report
/report-subcommand
/triggers
/subcommands/report

View File

@@ -0,0 +1,13 @@
GOARCH ?= amd64
TRIGGERS = triggers/report
BUILD = report-subcommand triggers
PLUGIN_NAME = traefik-vhosts
clean-report-subcommand:
rm -rf report-subcommand
report-subcommand: clean-report-subcommand src/subcommands/subcommands.go
GOARCH=$(GOARCH) go build -mod=readonly -ldflags="-s -w" $(GO_ARGS) -o report-subcommand src/subcommands/subcommands.go
ln -sf ../report-subcommand subcommands/report
include ../../common.mk

125
plugins/traefik-vhosts/command-functions Executable file → Normal file
View File

@@ -30,131 +30,6 @@ cmd-traefik-labels-show() {
cmd-proxy-labels-show "proxy:labels:show" "traefik" "$@"
}
cmd-traefik-report() {
declare desc="displays a traefik report for one or more apps"
declare cmd="traefik:report"
[[ "$1" == "$cmd" ]] && shift 1
fn-report-parse-args "$@"
set -- "${REPORT_ARGS[@]}"
declare APP="${1:-}" INFO_FLAG="${2:-}"
if [[ -n "$APP" ]] && [[ "$APP" == --* ]]; then
INFO_FLAG="$APP"
APP=""
fi
if [[ "$REPORT_IS_GLOBAL" == "true" ]]; then
APP="--global"
fi
if [[ -z "$APP" ]] && [[ -z "$INFO_FLAG" ]]; then
INFO_FLAG="true"
fi
if [[ "$APP" == "--global" ]]; then
cmd-traefik-report-single "$APP" "$INFO_FLAG" "$REPORT_FORMAT"
elif [[ -z "$APP" ]]; then
for app in $(dokku_apps); do
cmd-traefik-report-single "$app" "$INFO_FLAG" "$REPORT_FORMAT" | tee || true
done
else
cmd-traefik-report-single "$APP" "$INFO_FLAG" "$REPORT_FORMAT"
fi
}
cmd-traefik-report-single() {
declare APP="$1" INFO_FLAG="$2" FORMAT="${3:-stdout}"
if [[ "$INFO_FLAG" == "true" ]]; then
INFO_FLAG=""
fi
if [[ "$APP" != "--global" ]]; then
verify_app_name "$APP"
fi
local flag_map=(
"--traefik-computed-api-enabled: $(fn-traefik-computed-api-enabled)"
"--traefik-computed-api-entry-point: $(fn-traefik-computed-api-entry-point)"
"--traefik-computed-api-entry-point-address: $(fn-traefik-computed-api-entry-point-address)"
"--traefik-computed-api-vhost: $(fn-traefik-computed-api-vhost)"
"--traefik-computed-basic-auth-password: $(fn-traefik-computed-basic-auth-password)"
"--traefik-computed-basic-auth-username: $(fn-traefik-computed-basic-auth-username)"
"--traefik-computed-challenge-mode: $(fn-traefik-computed-challenge-mode)"
"--traefik-computed-dashboard-enabled: $(fn-traefik-computed-dashboard-enabled)"
"--traefik-computed-dns-provider: $(fn-traefik-computed-dns-provider)"
"--traefik-computed-http-entry-point: $(fn-traefik-computed-http-entry-point)"
"--traefik-computed-https-entry-point: $(fn-traefik-computed-https-entry-point)"
"--traefik-computed-image: $(fn-traefik-computed-image)"
"--traefik-computed-letsencrypt-email: $(fn-traefik-computed-letsencrypt-email)"
"--traefik-computed-letsencrypt-server: $(fn-traefik-computed-letsencrypt-server)"
"--traefik-computed-log-level: $(fn-traefik-computed-log-level)"
"--traefik-global-api-enabled: $(fn-traefik-global-api-enabled)"
"--traefik-global-api-entry-point: $(fn-traefik-global-api-entry-point)"
"--traefik-global-api-entry-point-address: $(fn-traefik-global-api-entry-point-address)"
"--traefik-global-api-vhost: $(fn-traefik-global-api-vhost)"
"--traefik-global-basic-auth-password: $(fn-traefik-global-basic-auth-password)"
"--traefik-global-basic-auth-username: $(fn-traefik-global-basic-auth-username)"
"--traefik-global-challenge-mode: $(fn-traefik-global-challenge-mode)"
"--traefik-global-dashboard-enabled: $(fn-traefik-global-dashboard-enabled)"
"--traefik-global-dns-provider: $(fn-traefik-global-dns-provider)"
"--traefik-global-http-entry-point: $(fn-traefik-global-http-entry-point)"
"--traefik-global-https-entry-point: $(fn-traefik-global-https-entry-point)"
"--traefik-global-image: $(fn-traefik-global-image)"
"--traefik-global-letsencrypt-email: $(fn-traefik-global-letsencrypt-email)"
"--traefik-global-letsencrypt-server: $(fn-traefik-global-letsencrypt-server)"
"--traefik-global-log-level: $(fn-traefik-global-log-level)"
)
local dns_provider_env_vars
dns_provider_env_vars="$(fn-traefik-dns-provider-env-vars)"
if [[ -n "$dns_provider_env_vars" ]]; then
while IFS= read -r line; do
local key value
key="$(echo "$line" | cut -d' ' -f1)"
value="$(echo "$line" | cut -d' ' -f2-)"
if [[ -n "$key" ]]; then
if [[ "$FORMAT" == "json" ]] || [[ "$INFO_FLAG" == "--traefik-${key}" ]]; then
flag_map+=("--traefik-${key}: ${value}")
else
flag_map+=("--traefik-${key}: *******")
fi
fi
done <<<"$dns_provider_env_vars"
fi
fn-report-validate-format "$FORMAT" "$INFO_FLAG"
if [[ "$FORMAT" == "json" ]]; then
fn-report-emit-json flag_map "traefik"
return
fi
if [[ -z "$INFO_FLAG" ]]; then
if [[ "$APP" == "--global" ]]; then
dokku_log_info2_quiet "global traefik information"
else
dokku_log_info2_quiet "${APP} traefik information"
fi
for flag in "${flag_map[@]}"; do
key="$(echo "${flag#--}" | cut -f1 -d' ' | tr - ' ')"
dokku_log_verbose "$(printf "%-30s %-25s" "${key^}" "${flag#*: }")"
done
else
local match=false
for flag in "${flag_map[@]}"; do
valid_flags="${valid_flags} $(echo "$flag" | cut -d':' -f1)"
if [[ "$flag" == "${INFO_FLAG}:"* ]]; then
value=${flag#*: }
size="${#value}"
if [[ "$size" -ne 0 ]]; then
echo "$value" && match=true
else
match=true
fi
fi
done
[[ "$match" == "true" ]] || dokku_log_fail "Invalid flag passed, valid flags:${valid_flags}"
fi
}
cmd-traefik-logs() {
declare desc="display traefik logs from command line"
declare cmd="traefik:logs"

View File

@@ -0,0 +1,29 @@
module github.com/dokku/dokku/plugins/traefik-vhosts
go 1.26.2
require (
github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000
github.com/spf13/pflag v1.0.10
)
require (
github.com/alexellis/go-execute/v2 v2.2.1 // indirect
github.com/fatih/color v1.19.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/melbahja/goph v1.5.1 // indirect
github.com/otiai10/copy v1.14.1 // indirect
github.com/otiai10/mint v1.6.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/sftp v1.13.10 // indirect
github.com/ryanuber/columnize v2.1.2+incompatible // indirect
golang.org/x/crypto v0.53.0 // indirect
golang.org/x/sync v0.21.0 // indirect
golang.org/x/sys v0.46.0 // indirect
)
replace github.com/dokku/dokku/plugins/common => ../common

View File

@@ -0,0 +1,55 @@
github.com/alexellis/go-execute/v2 v2.2.1 h1:4Ye3jiCKQarstODOEmqDSRCqxMHLkC92Bhse743RdOI=
github.com/alexellis/go-execute/v2 v2.2.1/go.mod h1:FMdRnUTiFAmYXcv23txrp3VYZfLo24nMpiIneWgKHTQ=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/melbahja/goph v1.5.1 h1:gdZyjn5nM6FO6mAZrv7XqE2gArQbb+V6FdIyH3Ru3vA=
github.com/melbahja/goph v1.5.1/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I=
github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg=
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU=
github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk=
github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,6 +0,0 @@
#!/usr/bin/env bash
source "$PLUGIN_AVAILABLE_PATH/traefik-vhosts/command-functions"
set -eo pipefail
[[ $DOKKU_TRACE ]] && set -x
cmd-traefik-report-single "$@"

View File

@@ -0,0 +1,271 @@
package traefikvhosts
import (
"os"
"path/filepath"
"strings"
"github.com/dokku/dokku/plugins/common"
)
// ReportSingleApp is an internal function that displays the traefik report for one or more apps
func ReportSingleApp(appName string, format string, infoFlag string) error {
if appName != "--global" {
if err := common.VerifyAppName(appName); err != nil {
return err
}
}
flags := map[string]common.ReportFunc{
"--traefik-computed-api-enabled": reportComputedAPIEnabled,
"--traefik-computed-api-entry-point": reportComputedAPIEntryPoint,
"--traefik-computed-api-entry-point-address": reportComputedAPIEntryPointAddress,
"--traefik-computed-api-vhost": reportComputedAPIVhost,
"--traefik-computed-basic-auth-password": reportComputedBasicAuthPassword,
"--traefik-computed-basic-auth-username": reportComputedBasicAuthUsername,
"--traefik-computed-challenge-mode": reportComputedChallengeMode,
"--traefik-computed-dashboard-enabled": reportComputedDashboardEnabled,
"--traefik-computed-dns-provider": reportComputedDNSProvider,
"--traefik-computed-http-entry-point": reportComputedHTTPEntryPoint,
"--traefik-computed-https-entry-point": reportComputedHTTPSEntryPoint,
"--traefik-computed-image": reportComputedImage,
"--traefik-computed-letsencrypt-email": reportComputedLetsencryptEmail,
"--traefik-computed-letsencrypt-server": reportComputedLetsencryptServer,
"--traefik-computed-log-level": reportComputedLogLevel,
"--traefik-global-api-enabled": reportGlobalAPIEnabled,
"--traefik-global-api-entry-point": reportGlobalAPIEntryPoint,
"--traefik-global-api-entry-point-address": reportGlobalAPIEntryPointAddress,
"--traefik-global-api-vhost": reportGlobalAPIVhost,
"--traefik-global-basic-auth-password": reportGlobalBasicAuthPassword,
"--traefik-global-basic-auth-username": reportGlobalBasicAuthUsername,
"--traefik-global-challenge-mode": reportGlobalChallengeMode,
"--traefik-global-dashboard-enabled": reportGlobalDashboardEnabled,
"--traefik-global-dns-provider": reportGlobalDNSProvider,
"--traefik-global-http-entry-point": reportGlobalHTTPEntryPoint,
"--traefik-global-https-entry-point": reportGlobalHTTPSEntryPoint,
"--traefik-global-image": reportGlobalImage,
"--traefik-global-letsencrypt-email": reportGlobalLetsencryptEmail,
"--traefik-global-letsencrypt-server": reportGlobalLetsencryptServer,
"--traefik-global-log-level": reportGlobalLogLevel,
}
// dns-provider-* env vars are dynamic; their values are masked in the default
// stdout report, but shown for --format json or when queried explicitly by name
dnsProviderVars, err := common.PropertyGetAllByPrefix("traefik", "--global", "dns-provider-")
if err != nil {
return err
}
for key, value := range dnsProviderVars {
flagName := "--traefik-" + key
realValue := value
if format == "json" || infoFlag == flagName {
flags[flagName] = func(string) string { return realValue }
} else {
flags[flagName] = func(string) string { return "*******" }
}
}
flagKeys := []string{}
for flagKey := range flags {
flagKeys = append(flagKeys, flagKey)
}
infoFlags := common.CollectReport(appName, infoFlag, flags)
return common.ReportSingleApp(common.ReportSingleAppInput{
ReportType: "traefik",
AppName: appName,
InfoFlag: infoFlag,
InfoFlags: infoFlags,
InfoFlagKeys: flagKeys,
Format: format,
TrimPrefix: true,
UppercaseFirstCharacter: true,
EmitLegacyPrefix: false,
})
}
func reportGlobalAPIEnabled(appName string) string {
return common.PropertyGet("traefik", "--global", "api-enabled")
}
func reportComputedAPIEnabled(appName string) string {
value := reportGlobalAPIEnabled(appName)
if value == "" {
value = "false"
}
return value
}
func reportGlobalAPIEntryPoint(appName string) string {
return common.PropertyGet("traefik", "--global", "api-entry-point")
}
func reportComputedAPIEntryPoint(appName string) string {
return reportGlobalAPIEntryPoint(appName)
}
func reportGlobalAPIEntryPointAddress(appName string) string {
return common.PropertyGet("traefik", "--global", "api-entry-point-address")
}
func reportComputedAPIEntryPointAddress(appName string) string {
return reportGlobalAPIEntryPointAddress(appName)
}
func reportGlobalAPIVhost(appName string) string {
return common.PropertyGet("traefik", "--global", "api-vhost")
}
func reportComputedAPIVhost(appName string) string {
value := reportGlobalAPIVhost(appName)
if value == "" {
value = "traefik.dokku.me"
}
return value
}
func reportGlobalBasicAuthPassword(appName string) string {
return common.PropertyGet("traefik", "--global", "basic-auth-password")
}
func reportComputedBasicAuthPassword(appName string) string {
return reportGlobalBasicAuthPassword(appName)
}
func reportGlobalBasicAuthUsername(appName string) string {
return common.PropertyGet("traefik", "--global", "basic-auth-username")
}
func reportComputedBasicAuthUsername(appName string) string {
return reportGlobalBasicAuthUsername(appName)
}
func reportGlobalChallengeMode(appName string) string {
return common.PropertyGet("traefik", "--global", "challenge-mode")
}
func reportComputedChallengeMode(appName string) string {
value := reportGlobalChallengeMode(appName)
if value == "" {
value = "tls"
}
return value
}
func reportGlobalDashboardEnabled(appName string) string {
return common.PropertyGet("traefik", "--global", "dashboard-enabled")
}
func reportComputedDashboardEnabled(appName string) string {
value := reportGlobalDashboardEnabled(appName)
if value == "" {
value = "false"
}
return value
}
func reportGlobalDNSProvider(appName string) string {
return common.PropertyGet("traefik", "--global", "dns-provider")
}
func reportComputedDNSProvider(appName string) string {
return reportGlobalDNSProvider(appName)
}
func reportGlobalHTTPEntryPoint(appName string) string {
return common.PropertyGet("traefik", "--global", "http-entry-point")
}
func reportComputedHTTPEntryPoint(appName string) string {
value := reportGlobalHTTPEntryPoint(appName)
if value == "" {
value = "http"
}
return value
}
func reportGlobalHTTPSEntryPoint(appName string) string {
return common.PropertyGet("traefik", "--global", "https-entry-point")
}
func reportComputedHTTPSEntryPoint(appName string) string {
value := reportGlobalHTTPSEntryPoint(appName)
if value == "" {
value = "https"
}
return value
}
func reportGlobalImage(appName string) string {
return common.PropertyGet("traefik", "--global", "image")
}
func reportComputedImage(appName string) string {
value := reportGlobalImage(appName)
if value == "" {
value = dockerfileFromImage("traefik-vhosts")
}
return value
}
func reportGlobalLetsencryptEmail(appName string) string {
return common.PropertyGet("traefik", "--global", "letsencrypt-email")
}
func reportComputedLetsencryptEmail(appName string) string {
return reportGlobalLetsencryptEmail(appName)
}
func reportGlobalLetsencryptServer(appName string) string {
return common.PropertyGet("traefik", "--global", "letsencrypt-server")
}
func reportComputedLetsencryptServer(appName string) string {
value := reportGlobalLetsencryptServer(appName)
if value == "" {
value = "https://acme-v02.api.letsencrypt.org/directory"
}
return value
}
func reportGlobalLogLevel(appName string) string {
return common.PropertyGet("traefik", "--global", "log-level")
}
func reportComputedLogLevel(appName string) string {
value := reportGlobalLogLevel(appName)
if value == "" {
value = "ERROR"
}
return strings.ToUpper(value)
}
// dockerfileFromImage returns the image referenced by the FROM line of the named
// plugin's Dockerfile, or an empty string when it cannot be read
func dockerfileFromImage(pluginName string) string {
pluginPath := os.Getenv("PLUGIN_AVAILABLE_PATH")
data, err := os.ReadFile(filepath.Join(pluginPath, pluginName, "Dockerfile"))
if err != nil {
return ""
}
for _, line := range strings.Split(string(data), "\n") {
if strings.Contains(line, "FROM") {
fields := strings.Fields(line)
if len(fields) >= 2 {
return fields[1]
}
}
}
return ""
}

View File

@@ -0,0 +1,40 @@
package main
import (
"fmt"
"os"
"strings"
"github.com/dokku/dokku/plugins/common"
traefikvhosts "github.com/dokku/dokku/plugins/traefik-vhosts"
flag "github.com/spf13/pflag"
)
// main entrypoint to all subcommands
func main() {
parts := strings.Split(os.Args[0], "/")
subcommand := parts[len(parts)-1]
var err error
switch subcommand {
case "report":
args := flag.NewFlagSet("traefik-vhosts:report", flag.ExitOnError)
format := args.String("format", "stdout", "format: [ stdout | json ]")
reportArgs, flagErr := common.ParseReportArgs("traefik-vhosts", os.Args[2:])
if flagErr == nil {
args.Parse(reportArgs.OSArgs)
appName := args.Arg(0)
if reportArgs.IsGlobal {
appName = "--global"
}
err = traefikvhosts.CommandReport(appName, *format, reportArgs.InfoFlag)
}
default:
err = fmt.Errorf("Invalid plugin subcommand call: %s", subcommand)
}
if err != nil {
common.LogFailWithError(err)
}
}

View File

@@ -0,0 +1,31 @@
package main
import (
"flag"
"fmt"
"os"
"strings"
"github.com/dokku/dokku/plugins/common"
traefikvhosts "github.com/dokku/dokku/plugins/traefik-vhosts"
)
// main entrypoint to all triggers
func main() {
parts := strings.Split(os.Args[0], "/")
trigger := parts[len(parts)-1]
flag.Parse()
var err error
switch trigger {
case "report":
appName := flag.Arg(0)
err = traefikvhosts.ReportSingleApp(appName, "", "")
default:
err = fmt.Errorf("Invalid plugin trigger call: %s", trigger)
}
if err != nil {
common.LogFailWithError(err)
}
}

View File

@@ -0,0 +1,29 @@
package traefikvhosts
import (
"errors"
"github.com/dokku/dokku/plugins/common"
)
// CommandReport displays a traefik-vhosts report for one or more apps
func CommandReport(appName string, format string, infoFlag string) error {
if len(appName) == 0 {
apps, err := common.DokkuApps()
if err != nil {
if errors.Is(err, common.NoAppsExist) {
common.LogWarn(err.Error())
return nil
}
return err
}
for _, appName := range apps {
if err := ReportSingleApp(appName, format, infoFlag); err != nil {
return err
}
}
return nil
}
return ReportSingleApp(appName, format, infoFlag)
}

View File

@@ -1,6 +0,0 @@
#!/usr/bin/env bash
source "$PLUGIN_AVAILABLE_PATH/traefik-vhosts/command-functions"
set -eo pipefail
[[ $DOKKU_TRACE ]] && set -x
cmd-traefik-report "$@"