feat: port certs :report subcommand to golang

The bash :report implementation for the certs plugin is replaced with a compiled golang binary. The certificate inspection getters run openssl and reproduce the existing field extraction, so the ssl report keys are unchanged, while collection now happens in parallel and json is marshalled directly. The now-unused fn-ssl-* display helpers are dropped, and fn-certs-set and fn-certs-remove remain for certs:add and certs:remove.
This commit is contained in:
Jose Diaz-Gonzalez
2026-07-07 06:56:51 -04:00
parent 8d0c36bad6
commit 6e5afb84a9
12 changed files with 435 additions and 174 deletions

4
plugins/certs/.gitignore vendored Normal file
View File

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

13
plugins/certs/Makefile Normal file
View File

@@ -0,0 +1,13 @@
GOARCH ?= amd64
TRIGGERS = triggers/report
BUILD = report-subcommand triggers
PLUGIN_NAME = certs
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

29
plugins/certs/go.mod Normal file
View File

@@ -0,0 +1,29 @@
module github.com/dokku/dokku/plugins/certs
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

55
plugins/certs/go.sum Normal file
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=

162
plugins/certs/internal-functions Executable file → Normal file
View File

@@ -46,165 +46,3 @@ fn-certs-remove() {
plugn trigger post-domains-update "$APP"
}
cmd-certs-report() {
declare desc="displays an ssl report for one or more apps"
declare cmd="certs: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-certs-report-single "$APP" "$INFO_FLAG" "$REPORT_FORMAT"
elif [[ -z "$APP" ]]; then
for app in $(dokku_apps); do
cmd-certs-report-single "$app" "$INFO_FLAG" "$REPORT_FORMAT" | tee || true
done
else
cmd-certs-report-single "$APP" "$INFO_FLAG" "$REPORT_FORMAT"
fi
}
cmd-certs-report-single() {
declare APP="$1" INFO_FLAG="$2" FORMAT="${3:-stdout}"
if [[ "$INFO_FLAG" == "true" ]]; then
INFO_FLAG=""
fi
local flag_map=()
if [[ "$APP" != "--global" ]]; then
verify_app_name "$APP"
flag_map=(
"--ssl-dir: $DOKKU_ROOT/$APP/tls"
"--ssl-enabled: $(fn-ssl-enabled "$APP")"
"--ssl-hostnames: $(fn-ssl-hostnames "$APP")"
"--ssl-expires-at: $(fn-ssl-expires-at "$APP")"
"--ssl-issuer: $(fn-ssl-issuer "$APP")"
"--ssl-starts-at: $(fn-ssl-starts-at "$APP")"
"--ssl-subject: $(fn-ssl-subject "$APP")"
"--ssl-verified: $(fn-ssl-verified "$APP")"
)
fi
fn-report-validate-format "$FORMAT" "$INFO_FLAG"
if [[ "$FORMAT" == "json" ]]; then
fn-report-emit-json flag_map "ssl"
return
fi
if [[ -z "$INFO_FLAG" ]]; then
if [[ "$APP" == "--global" ]]; then
dokku_log_info2_quiet "global ssl information"
else
dokku_log_info2_quiet "$APP ssl 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
}
fn-ssl-enabled() {
declare APP="$1"
local SSL_ENABLED=false
if is_ssl_enabled "$APP"; then
SSL_ENABLED=true
fi
echo "$SSL_ENABLED"
}
fn-ssl-expires-at() {
declare APP="$1"
local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls"
if is_ssl_enabled "$APP"; then
openssl x509 -in "$APP_SSL_PATH/server.crt" -noout -text | grep "Not After :" | awk -F " : " '{ print $2 }'
fi
}
fn-ssl-hostnames() {
declare APP="$1"
if is_ssl_enabled "$APP"; then
get_ssl_hostnames "$APP" | xargs
fi
}
fn-ssl-issuer() {
declare APP="$1"
local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls"
if is_ssl_enabled "$APP"; then
openssl x509 -in "$APP_SSL_PATH/server.crt" -noout -text | grep "Issuer:" | head -n1 | sed -e 's/Issuer: //g' -e 's/^[[:space:]]*//'
fi
}
fn-ssl-starts-at() {
declare APP="$1"
local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls"
if is_ssl_enabled "$APP"; then
openssl x509 -in "$APP_SSL_PATH/server.crt" -noout -text | grep "Not Before:" | awk -F ": " '{ print $2 }'
fi
}
fn-ssl-subject() {
declare APP="$1"
local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls"
if is_ssl_enabled "$APP"; then
openssl x509 -in "$APP_SSL_PATH/server.crt" -noout -subject | sed -e "s:subject= ::g" | sed -e "s:^/::g" | sed -e "s:/:; :g"
fi
}
fn-ssl-verified() {
declare APP="$1"
local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls"
local SSL_VERIFY_OUTPUT=false SSL_SELF_SIGNED="self signed"
if ! is_ssl_enabled "$APP"; then
return
fi
if [[ -f "$APP_SSL_PATH/server.letsencrypt.crt" ]]; then
SSL_VERIFY_OUTPUT="$(openssl verify -verbose -purpose sslserver -CAfile "$APP_SSL_PATH/server.crt" "$APP_SSL_PATH/server.letsencrypt.crt" 2>/dev/null | awk -F ':' '{ print $2 }' | tail -1 | xargs || true)"
else
SSL_VERIFY_OUTPUT="$(openssl verify -verbose -purpose sslserver "$APP_SSL_PATH/server.crt" 2>/dev/null | awk -F ':' '{ print $2 }' | tail -1 | xargs || true)"
fi
if [[ "$SSL_VERIFY_OUTPUT" == "OK" ]]; then
SSL_SELF_SIGNED="verified by a certificate authority"
fi
echo "$SSL_SELF_SIGNED"
}

View File

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

233
plugins/certs/report.go Normal file
View File

@@ -0,0 +1,233 @@
package certs
import (
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"github.com/dokku/dokku/plugins/common"
)
var dnsPrefixRegex = regexp.MustCompile(`[[:space:]]*DNS:`)
// ReportSingleApp is an internal function that displays the ssl report for one or more apps
func ReportSingleApp(appName string, format string, infoFlag string) error {
flags := map[string]common.ReportFunc{}
if appName != "--global" {
if err := common.VerifyAppName(appName); err != nil {
return err
}
flags = map[string]common.ReportFunc{
"--ssl-dir": reportSSLDir,
"--ssl-enabled": reportSSLEnabled,
"--ssl-hostnames": reportSSLHostnames,
"--ssl-expires-at": reportSSLExpiresAt,
"--ssl-issuer": reportSSLIssuer,
"--ssl-starts-at": reportSSLStartsAt,
"--ssl-subject": reportSSLSubject,
"--ssl-verified": reportSSLVerified,
}
}
flagKeys := []string{}
for flagKey := range flags {
flagKeys = append(flagKeys, flagKey)
}
infoFlags := common.CollectReport(appName, infoFlag, flags)
return common.ReportSingleApp(common.ReportSingleAppInput{
ReportType: "ssl",
AppName: appName,
InfoFlag: infoFlag,
InfoFlags: infoFlags,
InfoFlagKeys: flagKeys,
Format: format,
TrimPrefix: true,
UppercaseFirstCharacter: true,
EmitLegacyPrefix: false,
})
}
func certTLSPath(appName string) string {
return filepath.Join(common.MustGetEnv("DOKKU_ROOT"), appName, "tls")
}
func isSSLEnabled(appName string) bool {
tlsPath := certTLSPath(appName)
if _, err := os.Stat(filepath.Join(tlsPath, "server.crt")); err != nil {
return false
}
if _, err := os.Stat(filepath.Join(tlsPath, "server.key")); err != nil {
return false
}
return true
}
func opensslCertText(appName string) string {
result, err := common.CallExecCommand(common.ExecCommandInput{
Command: "openssl",
Args: []string{"x509", "-in", filepath.Join(certTLSPath(appName), "server.crt"), "-noout", "-text"},
})
if err != nil {
return ""
}
return result.Stdout
}
func reportSSLDir(appName string) string {
return certTLSPath(appName)
}
func reportSSLEnabled(appName string) string {
if isSSLEnabled(appName) {
return "true"
}
return "false"
}
func reportSSLExpiresAt(appName string) string {
if !isSSLEnabled(appName) {
return ""
}
for _, line := range strings.Split(opensslCertText(appName), "\n") {
if strings.Contains(line, "Not After :") {
parts := strings.Split(line, " : ")
if len(parts) >= 2 {
return parts[1]
}
}
}
return ""
}
func reportSSLStartsAt(appName string) string {
if !isSSLEnabled(appName) {
return ""
}
for _, line := range strings.Split(opensslCertText(appName), "\n") {
if strings.Contains(line, "Not Before:") {
parts := strings.Split(line, ": ")
if len(parts) >= 2 {
return parts[1]
}
}
}
return ""
}
func reportSSLIssuer(appName string) string {
if !isSSLEnabled(appName) {
return ""
}
for _, line := range strings.Split(opensslCertText(appName), "\n") {
if strings.Contains(line, "Issuer:") {
line = strings.Replace(line, "Issuer: ", "", 1)
return strings.TrimLeft(line, " \t")
}
}
return ""
}
func reportSSLSubject(appName string) string {
if !isSSLEnabled(appName) {
return ""
}
result, err := common.CallExecCommand(common.ExecCommandInput{
Command: "openssl",
Args: []string{"x509", "-in", filepath.Join(certTLSPath(appName), "server.crt"), "-noout", "-subject"},
})
if err != nil {
return ""
}
subject := strings.Replace(result.StdoutContents(), "subject= ", "", 1)
subject = strings.TrimPrefix(subject, "/")
return strings.ReplaceAll(subject, "/", "; ")
}
func reportSSLVerified(appName string) string {
if !isSSLEnabled(appName) {
return ""
}
tlsPath := certTLSPath(appName)
args := []string{"verify", "-verbose", "-purpose", "sslserver"}
if _, err := os.Stat(filepath.Join(tlsPath, "server.letsencrypt.crt")); err == nil {
args = append(args, "-CAfile", filepath.Join(tlsPath, "server.crt"), filepath.Join(tlsPath, "server.letsencrypt.crt"))
} else {
args = append(args, filepath.Join(tlsPath, "server.crt"))
}
result, _ := common.CallExecCommand(common.ExecCommandInput{
Command: "openssl",
Args: args,
})
verifyOutput := ""
if out := strings.TrimRight(result.Stdout, "\n"); out != "" {
lines := strings.Split(out, "\n")
parts := strings.Split(lines[len(lines)-1], ":")
if len(parts) >= 2 {
verifyOutput = strings.TrimSpace(parts[1])
}
}
if verifyOutput == "OK" {
return "verified by a certificate authority"
}
return "self signed"
}
func reportSSLHostnames(appName string) string {
if !isSSLEnabled(appName) {
return ""
}
hostnameSet := map[string]bool{}
subjectResult, err := common.CallExecCommand(common.ExecCommandInput{
Command: "openssl",
Args: []string{"x509", "-in", filepath.Join(certTLSPath(appName), "server.crt"), "-noout", "-subject"},
})
if err == nil {
for _, part := range strings.Split(subjectResult.StdoutContents(), "/") {
if strings.Contains(part, "CN=") && len(part) > 3 {
hostnameSet[part[3:]] = true
}
}
}
textLines := strings.Split(opensslCertText(appName), "\n")
for i, line := range textLines {
if strings.Contains(line, "509v3 Subject Alternative Name:") && i+1 < len(textLines) {
sanLine := dnsPrefixRegex.ReplaceAllString(textLines[i+1], "")
for _, name := range strings.Split(sanLine, ",") {
name = strings.TrimSpace(name)
if name != "" {
hostnameSet[name] = true
}
}
}
}
hostnames := []string{}
for name := range hostnameSet {
hostnames = append(hostnames, name)
}
sort.Strings(hostnames)
return strings.Join(hostnames, " ")
}

View File

@@ -0,0 +1,40 @@
package main
import (
"fmt"
"os"
"strings"
certs "github.com/dokku/dokku/plugins/certs"
"github.com/dokku/dokku/plugins/common"
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("certs:report", flag.ExitOnError)
format := args.String("format", "stdout", "format: [ stdout | json ]")
reportArgs, flagErr := common.ParseReportArgs("certs", os.Args[2:])
if flagErr == nil {
args.Parse(reportArgs.OSArgs)
appName := args.Arg(0)
if reportArgs.IsGlobal {
appName = "--global"
}
err = certs.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"
certs "github.com/dokku/dokku/plugins/certs"
"github.com/dokku/dokku/plugins/common"
)
// 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 = certs.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 certs
import (
"errors"
"github.com/dokku/dokku/plugins/common"
)
// CommandReport displays a certs 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/certs/internal-functions"
set -eo pipefail
[[ $DOKKU_TRACE ]] && set -x
cmd-certs-report "$@"