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:
173
plugins/openresty-vhosts/report.go
Normal file
173
plugins/openresty-vhosts/report.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package openrestyvhosts
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
)
|
||||
|
||||
const openrestyLogRoot = "/var/log/nginx"
|
||||
|
||||
// openrestyProp describes a settable openresty property along with the computed
|
||||
// default used when neither an app-scoped nor a global value is present.
|
||||
type openrestyProp struct {
|
||||
name string
|
||||
hasApp bool
|
||||
computedDefault func(appName string) string
|
||||
}
|
||||
|
||||
func staticDefault(value string) func(string) string {
|
||||
return func(string) string { return value }
|
||||
}
|
||||
|
||||
func openrestyPageSizeKB() int {
|
||||
return os.Getpagesize() / 1024
|
||||
}
|
||||
|
||||
func openrestyProps() []openrestyProp {
|
||||
pageSize := openrestyPageSizeKB()
|
||||
return []openrestyProp{
|
||||
{"access-log-format", true, staticDefault("")},
|
||||
{"access-log-path", true, func(appName string) string {
|
||||
return fmt.Sprintf("%s/%s-access.log", openrestyLogRoot, appName)
|
||||
}},
|
||||
{"bind-address-ipv4", true, staticDefault("")},
|
||||
{"bind-address-ipv6", true, staticDefault("::")},
|
||||
{"client-body-timeout", true, staticDefault("60s")},
|
||||
{"client-header-timeout", true, staticDefault("60s")},
|
||||
{"client-max-body-size", true, staticDefault("1m")},
|
||||
{"error-log-path", true, func(appName string) string {
|
||||
return fmt.Sprintf("%s/%s-error.log", openrestyLogRoot, appName)
|
||||
}},
|
||||
{"hsts-include-subdomains", true, staticDefault("true")},
|
||||
{"hsts-max-age", true, staticDefault("15724800")},
|
||||
{"hsts-preload", true, staticDefault("false")},
|
||||
{"image", false, func(string) string { return dockerfileFromImage("openresty-vhosts") }},
|
||||
{"keepalive-timeout", true, staticDefault("75s")},
|
||||
{"letsencrypt-email", false, staticDefault("")},
|
||||
{"letsencrypt-server", false, staticDefault("https://acme-v02.api.letsencrypt.org/directory")},
|
||||
{"lingering-timeout", true, staticDefault("5s")},
|
||||
{"log-level", false, staticDefault("ERROR")},
|
||||
{"proxy-buffer-size", true, staticDefault(fmt.Sprintf("%dk", pageSize))},
|
||||
{"proxy-buffering", true, staticDefault("on")},
|
||||
{"proxy-buffers", true, staticDefault(fmt.Sprintf("8 %dk", pageSize))},
|
||||
{"proxy-busy-buffers-size", true, staticDefault(fmt.Sprintf("%dk", pageSize*2))},
|
||||
{"proxy-connect-timeout", true, staticDefault("60s")},
|
||||
{"proxy-read-timeout", true, staticDefault("60s")},
|
||||
{"proxy-send-timeout", true, staticDefault("60s")},
|
||||
{"send-timeout", true, staticDefault("60s")},
|
||||
{"underscore-in-headers", true, staticDefault("off")},
|
||||
{"x-forwarded-for-value", true, staticDefault("$remote_addr")},
|
||||
{"x-forwarded-port-value", true, staticDefault("$server_port")},
|
||||
{"x-forwarded-proto-value", true, staticDefault("$scheme")},
|
||||
{"x-forwarded-ssl", true, staticDefault("")},
|
||||
}
|
||||
}
|
||||
|
||||
// ReportSingleApp is an internal function that displays the openresty 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
|
||||
}
|
||||
}
|
||||
isGlobal := appName == "--global"
|
||||
|
||||
flags := map[string]common.ReportFunc{}
|
||||
for _, p := range openrestyProps() {
|
||||
prop := p.name
|
||||
hasApp := p.hasApp
|
||||
computedDefault := p.computedDefault
|
||||
if hasApp && !isGlobal {
|
||||
flags["--openresty-"+prop] = func(app string) string {
|
||||
return common.PropertyGet("openresty", app, prop)
|
||||
}
|
||||
}
|
||||
flags["--openresty-global-"+prop] = func(app string) string {
|
||||
return common.PropertyGet("openresty", "--global", prop)
|
||||
}
|
||||
flags["--openresty-computed-"+prop] = func(app string) string {
|
||||
value := ""
|
||||
if hasApp && app != "--global" {
|
||||
value = common.PropertyGet("openresty", app, prop)
|
||||
}
|
||||
if value == "" {
|
||||
value = common.PropertyGet("openresty", "--global", prop)
|
||||
}
|
||||
if value == "" {
|
||||
value = computedDefault(app)
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
flags["--openresty-global-allowed-letsencrypt-domains-func-base64"] = func(app string) string {
|
||||
return common.PropertyGet("openresty", "--global", "allowed-letsencrypt-domains-func-base64")
|
||||
}
|
||||
flags["--openresty-computed-allowed-letsencrypt-domains-func-base64"] = func(app string) string {
|
||||
value := common.PropertyGetDefault("openresty", "--global", "allowed-letsencrypt-domains-func-base64", "return true")
|
||||
return base64.StdEncoding.EncodeToString([]byte(value))
|
||||
}
|
||||
|
||||
if !isGlobal {
|
||||
flags["--openresty-hsts"] = func(app string) string {
|
||||
return common.PropertyGet("openresty", app, "hsts")
|
||||
}
|
||||
}
|
||||
flags["--openresty-global-hsts"] = func(app string) string {
|
||||
return common.PropertyGet("openresty", "--global", "hsts")
|
||||
}
|
||||
flags["--openresty-computed-hsts"] = openrestyHstsIsEnabled
|
||||
|
||||
flagKeys := []string{}
|
||||
for flagKey := range flags {
|
||||
flagKeys = append(flagKeys, flagKey)
|
||||
}
|
||||
|
||||
infoFlags := common.CollectReport(appName, infoFlag, flags)
|
||||
return common.ReportSingleApp(common.ReportSingleAppInput{
|
||||
ReportType: "openresty",
|
||||
AppName: appName,
|
||||
InfoFlag: infoFlag,
|
||||
InfoFlags: infoFlags,
|
||||
InfoFlagKeys: flagKeys,
|
||||
Format: format,
|
||||
TrimPrefix: true,
|
||||
UppercaseFirstCharacter: true,
|
||||
EmitLegacyPrefix: false,
|
||||
})
|
||||
}
|
||||
|
||||
func openrestyHstsIsEnabled(appName string) string {
|
||||
value := common.PropertyGet("openresty", appName, "hsts")
|
||||
if value == "" {
|
||||
value = common.PropertyGetDefault("openresty", "--global", "hsts", "true")
|
||||
}
|
||||
|
||||
return 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 ""
|
||||
}
|
||||
Reference in New Issue
Block a user