Merge pull request #8934 from dokku/8928-traefik-report-does-not-surface-the-dns-provider-properties

Report traefik dns-provider env vars as global keys
This commit is contained in:
Jose Diaz-Gonzalez
2026-08-12 04:52:58 -04:00
committed by GitHub
7 changed files with 275 additions and 24 deletions

View File

@@ -23,7 +23,7 @@
- All `:report` subcommands now accept the `--global` flag, which scopes the report to globally-configured properties. The flag composes with `--format json`, so a JSON report of global properties can be obtained via, for example, `dokku scheduler:report --global --format json`. Previously, combining `--global` with `--format json` was rejected with an "info flag" error, and `--global` on its own was treated as an unknown flag. - All `:report` subcommands now accept the `--global` flag, which scopes the report to globally-configured properties. The flag composes with `--format json`, so a JSON report of global properties can be obtained via, for example, `dokku scheduler:report --global --format json`. Previously, combining `--global` with `--format json` was rejected with an "info flag" error, and `--global` on its own was treated as an unknown flag.
- Every `:report` key of the form `<plugin>-global-<property>` now returns the raw stored global value (empty when the property has never been set) instead of the resolved value with the built-in default substituted in. A new `<plugin>-computed-<property>` key has been added wherever a default existed and returns the effective value (per-app value, falling back to the global value, then to the built-in default). This affects `app-json`, `builder-dockerfile`, `builder-herokuish`, `builder-lambda`, `builder-nixpacks`, `builder-pack`, `builder-railpack`, `caddy`, `checks`, `git`, `haproxy`, `logs`, `network`, `nginx`, `openresty`, `proxy`, `registry`, `scheduler`, `scheduler-k3s` and `traefik`. External tooling that read `<plugin>-global-<property>` and depended on the default value should switch to `<plugin>-computed-<property>`. The bare `<plugin>-<property>` keys for caddy/haproxy/traefik/openresty global-only properties (`caddy-image`, `haproxy-log-level`, `traefik-api-enabled`, `openresty-image`, `openresty-letsencrypt-email`, `openresty-letsencrypt-server`, `openresty-allowed-letsencrypt-domains-func-base64`, etc.) have also been replaced by the `global-` and `computed-` pair. - Every `:report` key of the form `<plugin>-global-<property>` now returns the raw stored global value (empty when the property has never been set) instead of the resolved value with the built-in default substituted in. A new `<plugin>-computed-<property>` key has been added wherever a default existed and returns the effective value (per-app value, falling back to the global value, then to the built-in default). This affects `app-json`, `builder-dockerfile`, `builder-herokuish`, `builder-lambda`, `builder-nixpacks`, `builder-pack`, `builder-railpack`, `caddy`, `checks`, `git`, `haproxy`, `logs`, `network`, `nginx`, `openresty`, `proxy`, `registry`, `scheduler`, `scheduler-k3s` and `traefik`. External tooling that read `<plugin>-global-<property>` and depended on the default value should switch to `<plugin>-computed-<property>`. The bare `<plugin>-<property>` keys for caddy/haproxy/traefik/openresty global-only properties (`caddy-image`, `haproxy-log-level`, `traefik-api-enabled`, `openresty-image`, `openresty-letsencrypt-email`, `openresty-letsencrypt-server`, `openresty-allowed-letsencrypt-domains-func-base64`, etc.) have also been replaced by the `global-` and `computed-` pair.
- The `ps` and `cron` plugins now follow the same `<plugin>-global-<property>` / `<plugin>-computed-<property>` convention. The `ps:report` keys `stop-timeout-seconds`, `global-stop-timeout-seconds`, and `computed-stop-timeout-seconds` have been renamed to `ps-stop-timeout-seconds`, `ps-global-stop-timeout-seconds`, and `ps-computed-stop-timeout-seconds`. The `cron:report` keys `cron-mailfrom` and `cron-mailto` have been renamed to `cron-global-mailfrom` and `cron-global-mailto`, and new `cron-computed-mailfrom` and `cron-computed-mailto` keys have been added. The corresponding user-facing `--<flag>` arguments to `ps:report` and `cron:report` were renamed alongside the JSON keys; no aliases are kept. Additionally, both `ps:report --global` and `cron:report --global` now emit the `<plugin>-computed-<property>` keys for every settable global property with a default (`ps-computed-procfile-path`, `ps-computed-stop-timeout-seconds`, `cron-computed-maintenance`, `cron-computed-mailfrom`, `cron-computed-mailto`), matching the shape used by the other plugins. - The `ps` and `cron` plugins now follow the same `<plugin>-global-<property>` / `<plugin>-computed-<property>` convention. The `ps:report` keys `stop-timeout-seconds`, `global-stop-timeout-seconds`, and `computed-stop-timeout-seconds` have been renamed to `ps-stop-timeout-seconds`, `ps-global-stop-timeout-seconds`, and `ps-computed-stop-timeout-seconds`. The `cron:report` keys `cron-mailfrom` and `cron-mailto` have been renamed to `cron-global-mailfrom` and `cron-global-mailto`, and new `cron-computed-mailfrom` and `cron-computed-mailto` keys have been added. The corresponding user-facing `--<flag>` arguments to `ps:report` and `cron:report` were renamed alongside the JSON keys; no aliases are kept. Additionally, both `ps:report --global` and `cron:report --global` now emit the `<plugin>-computed-<property>` keys for every settable global property with a default (`ps-computed-procfile-path`, `ps-computed-stop-timeout-seconds`, `cron-computed-maintenance`, `cron-computed-mailfrom`, `cron-computed-mailto`), matching the shape used by the other plugins.
- A second round of `:report` additions surfaces every remaining settable-but-unreported property under the same raw/global/computed convention so external tooling can verify drift via `:report --format json` without falling back to a generic bash task. The `ps` plugin gains `--ps-dockerfile-start-cmd` and `--ps-computed-dockerfile-start-cmd`, `--ps-start-cmd` and `--ps-computed-start-cmd`, and the `--ps-skip-deploy` / `--ps-global-skip-deploy` / `--ps-computed-skip-deploy` triple (default `false`). The `builder` plugin gains the `--builder-skip-cleanup` triple (default `false`). The `scheduler` plugin gains the `--scheduler-shell` triple. The `proxy` plugin gains the `--proxy-proxy-port` and `--proxy-proxy-ssl-port` triples and exposes the raw `disabled` property as `--proxy-disabled` / `--proxy-computed-disabled`, alongside the existing inverted `--proxy-enabled`. The `openresty` plugin gains `--openresty-global-log-level` and `--openresty-computed-log-level` (default `ERROR`). The `nginx` plugin gains the `--nginx-nginx-service-command` triple. The `scheduler-k3s` plugin gains `--scheduler-k3s-global-token`, but the value is masked as `*******` in default stdout output; the raw value is returned only when the report is requested via `--format json` or when this flag is queried explicitly by name. The traefik `dns-provider-<env_var>` keys now follow the same explicit-query rule - previously they were unmasked only for `--format json`, but a query like `dokku traefik:report --traefik-dns-provider-cf_api_key` now returns the actual value instead of `*******`. - A second round of `:report` additions surfaces every remaining settable-but-unreported property under the same raw/global/computed convention so external tooling can verify drift via `:report --format json` without falling back to a generic bash task. The `ps` plugin gains `--ps-dockerfile-start-cmd` and `--ps-computed-dockerfile-start-cmd`, `--ps-start-cmd` and `--ps-computed-start-cmd`, and the `--ps-skip-deploy` / `--ps-global-skip-deploy` / `--ps-computed-skip-deploy` triple (default `false`). The `builder` plugin gains the `--builder-skip-cleanup` triple (default `false`). The `scheduler` plugin gains the `--scheduler-shell` triple. The `proxy` plugin gains the `--proxy-proxy-port` and `--proxy-proxy-ssl-port` triples and exposes the raw `disabled` property as `--proxy-disabled` / `--proxy-computed-disabled`, alongside the existing inverted `--proxy-enabled`. The `openresty` plugin gains `--openresty-global-log-level` and `--openresty-computed-log-level` (default `ERROR`). The `nginx` plugin gains the `--nginx-nginx-service-command` triple. The `scheduler-k3s` plugin gains `--scheduler-k3s-global-token`, but the value is masked as `*******` in default stdout output; the raw value is returned only when the report is requested via `--format json` or when this flag is queried explicitly by name. The traefik `dns-provider-<env_var>` keys are reported as `--traefik-global-dns-provider-<env_var>` and follow the same masking and explicit-query rules, as does the traefik `basic-auth-password` property, whose `--traefik-global-basic-auth-password` and `--traefik-computed-basic-auth-password` keys are masked in default stdout output.
- All Go-implemented plugins (`app-json`, `apps`, `builder`, `buildpacks`, `builds`, `cron`, `docker-options`, `logs`, `network`, `ports`, `proxy`, `ps`, `registry`, `resource`, `scheduler`, `scheduler-k3s`, `storage`) now emit JSON keys from `:report --format json` without the `<plugin>-` head segment, matching the shape bash plugins have always emitted. For example, `dokku ps:report myapp --format json` now contains `stop-timeout-seconds`, `global-stop-timeout-seconds`, and `computed-stop-timeout-seconds` keys. The CLI flag names (`--ps-stop-timeout-seconds`, etc.) are unchanged, and `:set` semantics are unchanged. For backwards compatibility during the 0.38.x patch series, the old `<plugin>-<property>` JSON keys are emitted side-by-side with the new keys, so external scripts reading either shape continue to work. The legacy keys will be dropped in a future major release. External JSON consumers should migrate to the new key shape. - All Go-implemented plugins (`app-json`, `apps`, `builder`, `buildpacks`, `builds`, `cron`, `docker-options`, `logs`, `network`, `ports`, `proxy`, `ps`, `registry`, `resource`, `scheduler`, `scheduler-k3s`, `storage`) now emit JSON keys from `:report --format json` without the `<plugin>-` head segment, matching the shape bash plugins have always emitted. For example, `dokku ps:report myapp --format json` now contains `stop-timeout-seconds`, `global-stop-timeout-seconds`, and `computed-stop-timeout-seconds` keys. The CLI flag names (`--ps-stop-timeout-seconds`, etc.) are unchanged, and `:set` semantics are unchanged. For backwards compatibility during the 0.38.x patch series, the old `<plugin>-<property>` JSON keys are emitted side-by-side with the new keys, so external scripts reading either shape continue to work. The legacy keys will be dropped in a future major release. External JSON consumers should migrate to the new key shape.
- The `scheduler-k3s` plugin now manages env config and the dokku-generated image pull Secret as their own helm releases with stable names (`config-{app}` and `pull-secret-{app}`) rather than bundling them into the app helm chart with a per-deploy timestamp suffix (`env-{app}.{ts}` / `ims-{app}.{ts}`). This fixes two bugs: a helm rollback of the app chart no longer deletes Secrets that older ReplicaSets still reference, and the Deployment's `imagePullSecrets` list no longer accumulates references to nonexistent Secrets across deploys. The next deploy of an app switches the Deployment's `envFrom` and `imagePullSecrets` references to the stable names and prunes any leaked entries; existing live Deployments do not need to be patched manually. App rename now also uninstalls the old `tls-{app}`, `config-{app}`, and `pull-secret-{app}` releases under the previous app name; the new name's releases are recreated on the next deploy or certs sync. - The `scheduler-k3s` plugin now manages env config and the dokku-generated image pull Secret as their own helm releases with stable names (`config-{app}` and `pull-secret-{app}`) rather than bundling them into the app helm chart with a per-deploy timestamp suffix (`env-{app}.{ts}` / `ims-{app}.{ts}`). This fixes two bugs: a helm rollback of the app chart no longer deletes Secrets that older ReplicaSets still reference, and the Deployment's `imagePullSecrets` list no longer accumulates references to nonexistent Secrets across deploys. The next deploy of an app switches the Deployment's `envFrom` and `imagePullSecrets` references to the stable names and prunes any leaked entries; existing live Deployments do not need to be patched manually. App rename now also uninstalls the old `tls-{app}`, `config-{app}`, and `pull-secret-{app}` releases under the previous app name; the new name's releases are recreated on the next deploy or certs sync.
- **New in 0.38.25:** Values supplied through docker options, `dokku run`'s `-e`/`--env` flag, and `--ttl-seconds` are no longer evaluated by the shell when assembling a container's arguments; they are now tokenized and passed through verbatim. This closes a command-injection vector where a `$(...)` or backtick expression in one of these values executed on the host as the `dokku` user during build, deploy, or run. As a result, shell metacharacters such as `$(...)`, backticks, `$VAR`, and globs in these values are treated literally instead of being expanded, and `--ttl-seconds` must now be a plain integer. Existing Traefik docker-options labels (those whose label key begins with `traefik.`) whose backticks were stored with a stray backslash are repaired automatically the first time `dokku` runs after the upgrade, so they become valid on the next deploy. - **New in 0.38.25:** Values supplied through docker options, `dokku run`'s `-e`/`--env` flag, and `--ttl-seconds` are no longer evaluated by the shell when assembling a container's arguments; they are now tokenized and passed through verbatim. This closes a command-injection vector where a `$(...)` or backtick expression in one of these values executed on the host as the `dokku` user during build, deploy, or run. As a result, shell metacharacters such as `$(...)`, backticks, `$VAR`, and globs in these values are treated literally instead of being expanded, and `--ttl-seconds` must now be a plain integer. Existing Traefik docker-options labels (those whose label key begins with `traefik.`) whose backticks were stored with a stray backslash are repaired automatically the first time `dokku` runs after the upgrade, so they become valid on the next deploy.

View File

@@ -260,6 +260,16 @@ dokku traefik:set --global dns-provider-cf_api_key your-api-key
The `dns-provider-` prefix will be stripped and the variable name will be uppercased when passed to the Traefik container. For example, `dns-provider-cf_api_email` becomes `CF_API_EMAIL`. The `dns-provider-` prefix will be stripped and the variable name will be uppercased when passed to the Traefik container. For example, `dns-provider-cf_api_email` becomes `CF_API_EMAIL`.
Each configured variable is surfaced by `traefik:report` as `--traefik-global-dns-provider-<env_var>`. As these values are provider credentials, they are masked as `*******` in the default report output, including the aggregate `dokku report`. The raw value is returned when the flag is requested explicitly or when the report is rendered as json:
```shell
dokku traefik:report --global --traefik-global-dns-provider-cf_api_email
```
```shell
dokku traefik:report --global --format json | jq -r '."global-dns-provider-cf_api_email"'
```
After configuring, the Traefik container will need to be restarted and apps will need to be rebuilt. After configuring, the Traefik container will need to be restarted and apps will need to be rebuilt.
Refer to the [Traefik DNS Challenge documentation](https://doc.traefik.io/traefik/https/acme/#dnschallenge) for the list of supported DNS providers and their required environment variables. Refer to the [Traefik DNS Challenge documentation](https://doc.traefik.io/traefik/https/acme/#dnschallenge) for the list of supported DNS providers and their required environment variables.
@@ -403,12 +413,12 @@ All traefik properties are global only. Set with `traefik:set --global <property
| `api-entry-point` | global only | none | `--traefik-global-api-entry-point`, `--traefik-computed-api-entry-point` | Name of the entry point used by the Traefik API | | `api-entry-point` | global only | none | `--traefik-global-api-entry-point`, `--traefik-computed-api-entry-point` | Name of the entry point used by the Traefik API |
| `api-entry-point-address` | global only | none | `--traefik-global-api-entry-point-address`, `--traefik-computed-api-entry-point-address` | Address (`host:port`) the Traefik API listens on | | `api-entry-point-address` | global only | none | `--traefik-global-api-entry-point-address`, `--traefik-computed-api-entry-point-address` | Address (`host:port`) the Traefik API listens on |
| `api-vhost` | global only | `traefik.dokku.me` | `--traefik-global-api-vhost`, `--traefik-computed-api-vhost` | Virtual host that routes to the Traefik API | | `api-vhost` | global only | `traefik.dokku.me` | `--traefik-global-api-vhost`, `--traefik-computed-api-vhost` | Virtual host that routes to the Traefik API |
| `basic-auth-password` | global only | none | `--traefik-global-basic-auth-password`, `--traefik-computed-basic-auth-password` | Password for basic auth in front of the API/dashboard | | `basic-auth-password` | global only | none | `--traefik-global-basic-auth-password`, `--traefik-computed-basic-auth-password` (masked as `*******` in the default stdout report; the raw value is returned when queried via `--format json` or when one of these flags is requested explicitly) | Password for basic auth in front of the API/dashboard |
| `basic-auth-username` | global only | none | `--traefik-global-basic-auth-username`, `--traefik-computed-basic-auth-username` | Username for basic auth in front of the API/dashboard | | `basic-auth-username` | global only | none | `--traefik-global-basic-auth-username`, `--traefik-computed-basic-auth-username` | Username for basic auth in front of the API/dashboard |
| `challenge-mode` | global only | `tls` | `--traefik-global-challenge-mode`, `--traefik-computed-challenge-mode` | ACME challenge method used by Traefik (`tls`, `http`, or `dns`) | | `challenge-mode` | global only | `tls` | `--traefik-global-challenge-mode`, `--traefik-computed-challenge-mode` | ACME challenge method used by Traefik (`tls`, `http`, or `dns`) |
| `dashboard-enabled` | global only | `false` | `--traefik-global-dashboard-enabled`, `--traefik-computed-dashboard-enabled` | When `true`, enables the Traefik dashboard | | `dashboard-enabled` | global only | `false` | `--traefik-global-dashboard-enabled`, `--traefik-computed-dashboard-enabled` | When `true`, enables the Traefik dashboard |
| `dns-provider` | global only | none | `--traefik-global-dns-provider`, `--traefik-computed-dns-provider` | Lego DNS provider name used when `challenge-mode` is `dns` | | `dns-provider` | global only | none | `--traefik-global-dns-provider`, `--traefik-computed-dns-provider` | Lego DNS provider name used when `challenge-mode` is `dns` |
| `dns-provider-<ENV_VAR>` | global only | none | `--traefik-dns-provider-<env_var>` (masked as `*******` in the default stdout report; the raw value is returned when queried via `--format json` or when this flag is requested explicitly) | Per-provider environment variables passed to the Traefik container; `<ENV_VAR>` is the upstream variable name (e.g. `dns-provider-cloudflare-api-token`) | | `dns-provider-<ENV_VAR>` | global only | none | `--traefik-global-dns-provider-<env_var>` (masked as `*******` in the default stdout report; the raw value is returned when queried via `--format json` or when this flag is requested explicitly) | Per-provider environment variables passed to the Traefik container; `<ENV_VAR>` is the upstream variable name (e.g. `dns-provider-cloudflare-api-token`) |
| `http-entry-point` | global only | `http` | `--traefik-global-http-entry-point`, `--traefik-computed-http-entry-point` | Entry point name handling plaintext HTTP traffic | | `http-entry-point` | global only | `http` | `--traefik-global-http-entry-point`, `--traefik-computed-http-entry-point` | Entry point name handling plaintext HTTP traffic |
| `https-entry-point` | global only | `https` | `--traefik-global-https-entry-point`, `--traefik-computed-https-entry-point` | Entry point name handling TLS-terminated HTTPS traffic | | `https-entry-point` | global only | `https` | `--traefik-global-https-entry-point`, `--traefik-computed-https-entry-point` | Entry point name handling TLS-terminated HTTPS traffic |
| `image` | global only | _parsed from `plugins/traefik-vhosts/Dockerfile`_ | `--traefik-global-image`, `--traefik-computed-image` | Docker image used to run the Traefik container | | `image` | global only | _parsed from `plugins/traefik-vhosts/Dockerfile`_ | `--traefik-global-image`, `--traefik-computed-image` | Docker image used to run the Traefik container |

View File

@@ -8,6 +8,9 @@ import (
"github.com/dokku/dokku/plugins/common" "github.com/dokku/dokku/plugins/common"
) )
// valueMask is shown in place of credential values in the default stdout report
const valueMask = "*******"
// ReportSingleApp is an internal function that displays the traefik report for one or more apps // ReportSingleApp is an internal function that displays the traefik report for one or more apps
func ReportSingleApp(appName string, format string, infoFlag string) error { func ReportSingleApp(appName string, format string, infoFlag string) error {
if appName != "--global" { if appName != "--global" {
@@ -49,20 +52,16 @@ func ReportSingleApp(appName string, format string, infoFlag string) error {
"--traefik-global-log-level": reportGlobalLogLevel, "--traefik-global-log-level": reportGlobalLogLevel,
} }
// dns-provider-* env vars are dynamic; their values are masked in the default for _, flagName := range []string{"--traefik-computed-basic-auth-password", "--traefik-global-basic-auth-password"} {
// stdout report, but shown for --format json or when queried explicitly by name flags[flagName] = maskedReportFunc(flags[flagName], flagName, format, infoFlag)
dnsProviderVars, err := common.PropertyGetAllByPrefix("traefik", "--global", "dns-provider-") }
dnsProviderFlags, err := dnsProviderReportFlags(format, infoFlag)
if err != nil { if err != nil {
return err return err
} }
for key, value := range dnsProviderVars { for flagName, reportFunc := range dnsProviderFlags {
flagName := "--traefik-" + key flags[flagName] = reportFunc
realValue := value
if format == "json" || infoFlag == flagName {
flags[flagName] = func(string) string { return realValue }
} else {
flags[flagName] = func(string) string { return "*******" }
}
} }
flagKeys := []string{} flagKeys := []string{}
@@ -84,6 +83,40 @@ func ReportSingleApp(appName string, format string, infoFlag string) error {
}) })
} }
// maskedReportFunc hides a credential value behind valueMask so the default stdout
// report - and thus the aggregate `dokku report` - never prints it. The raw value is
// returned for machine-readable output or when the flag is requested by name.
func maskedReportFunc(fn common.ReportFunc, flagName string, format string, infoFlag string) common.ReportFunc {
if format == "json" || infoFlag == flagName {
return fn
}
return func(appName string) string {
if fn(appName) == "" {
return ""
}
return valueMask
}
}
// dnsProviderReportFlags returns report functions for the dynamic dns-provider-*
// properties, keyed by their global report flag
func dnsProviderReportFlags(format string, infoFlag string) (map[string]common.ReportFunc, error) {
flags := map[string]common.ReportFunc{}
properties, err := common.PropertyGetAllByPrefix("traefik", "--global", "dns-provider-")
if err != nil {
return flags, err
}
for property, value := range properties {
flagName := "--traefik-global-" + property
flags[flagName] = maskedReportFunc(func(string) string { return value }, flagName, format, infoFlag)
}
return flags, nil
}
func reportGlobalAPIEnabled(appName string) string { func reportGlobalAPIEnabled(appName string) string {
return common.PropertyGet("traefik", "--global", "api-enabled") return common.PropertyGet("traefik", "--global", "api-enabled")
} }

View File

@@ -0,0 +1,143 @@
package traefikvhosts
import (
"os/user"
"path/filepath"
"testing"
"github.com/dokku/dokku/plugins/common"
)
// setupPropertyEnv points the dokku env at temporary directories and tells the
// permission helpers to chown files to the current user (a no-op) so the test
// works without root.
func setupPropertyEnv(t *testing.T) {
t.Helper()
libRoot := t.TempDir()
t.Setenv("DOKKU_LIB_ROOT", libRoot)
t.Setenv("DOKKU_ROOT", t.TempDir())
t.Setenv("PLUGIN_PATH", filepath.Join(libRoot, "plugins"))
current, err := user.Current()
if err != nil {
t.Fatalf("user.Current: %v", err)
}
group, err := user.LookupGroupId(current.Gid)
if err != nil {
t.Fatalf("user.LookupGroupId: %v", err)
}
t.Setenv("DOKKU_SYSTEM_USER", current.Username)
t.Setenv("DOKKU_SYSTEM_GROUP", group.Name)
}
func staticReportFunc(value string) common.ReportFunc {
return func(string) string {
return value
}
}
func TestMaskedReportFunc(t *testing.T) {
const flagName = "--traefik-global-basic-auth-password"
cases := []struct {
name string
value string
format string
infoFlag string
want string
}{
{name: "stdout masks a set value", value: "hunter2", format: "stdout", want: valueMask},
{name: "empty format masks a set value", value: "hunter2", want: valueMask},
{name: "stdout leaves an unset value empty", value: "", format: "stdout", want: ""},
{name: "json returns the raw value", value: "hunter2", format: "json", want: "hunter2"},
{name: "matching info flag returns the raw value", value: "hunter2", format: "stdout", infoFlag: flagName, want: "hunter2"},
{name: "other info flag masks the value", value: "hunter2", format: "stdout", infoFlag: "--traefik-global-basic-auth-username", want: valueMask},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
fn := maskedReportFunc(staticReportFunc(tc.value), flagName, tc.format, tc.infoFlag)
if got := fn("--global"); got != tc.want {
t.Errorf("maskedReportFunc(%q, %q, %q) = %q, want %q", tc.value, tc.format, tc.infoFlag, got, tc.want)
}
})
}
}
func TestDNSProviderReportFlags(t *testing.T) {
setupPropertyEnv(t)
if err := common.PropertyWrite("traefik", "--global", "dns-provider", "cloudflare"); err != nil {
t.Fatalf("PropertyWrite dns-provider: %v", err)
}
if err := common.PropertyWrite("traefik", "--global", "dns-provider-cf_api_email", "test@example.com"); err != nil {
t.Fatalf("PropertyWrite dns-provider-cf_api_email: %v", err)
}
if err := common.PropertyWrite("traefik", "--global", "dns-provider-cf_api_key", "secret-key"); err != nil {
t.Fatalf("PropertyWrite dns-provider-cf_api_key: %v", err)
}
flags, err := dnsProviderReportFlags("stdout", "")
if err != nil {
t.Fatalf("dnsProviderReportFlags: %v", err)
}
assertReportFuncValue(t, flags, "--traefik-global-dns-provider-cf_api_email", valueMask)
assertReportFuncValue(t, flags, "--traefik-global-dns-provider-cf_api_key", valueMask)
if len(flags) != 2 {
t.Errorf("flags = %v, want only the two dns-provider-* env var flags", flagNames(flags))
}
if _, ok := flags["--traefik-global-dns-provider"]; ok {
t.Error("--traefik-global-dns-provider should be reported statically, not as a dynamic env var")
}
if _, ok := flags["--traefik-dns-provider-cf_api_email"]; ok {
t.Error("--traefik-dns-provider-cf_api_email should not be emitted outside the global namespace")
}
flags, err = dnsProviderReportFlags("json", "")
if err != nil {
t.Fatalf("dnsProviderReportFlags: %v", err)
}
assertReportFuncValue(t, flags, "--traefik-global-dns-provider-cf_api_email", "test@example.com")
assertReportFuncValue(t, flags, "--traefik-global-dns-provider-cf_api_key", "secret-key")
flags, err = dnsProviderReportFlags("stdout", "--traefik-global-dns-provider-cf_api_key")
if err != nil {
t.Fatalf("dnsProviderReportFlags: %v", err)
}
assertReportFuncValue(t, flags, "--traefik-global-dns-provider-cf_api_email", valueMask)
assertReportFuncValue(t, flags, "--traefik-global-dns-provider-cf_api_key", "secret-key")
}
func TestDNSProviderReportFlagsWithoutProperties(t *testing.T) {
setupPropertyEnv(t)
flags, err := dnsProviderReportFlags("stdout", "")
if err != nil {
t.Fatalf("dnsProviderReportFlags: %v", err)
}
if len(flags) != 0 {
t.Errorf("flags = %v, want no flags", flagNames(flags))
}
}
func assertReportFuncValue(t *testing.T, flags map[string]common.ReportFunc, flagName string, want string) {
t.Helper()
fn, ok := flags[flagName]
if !ok {
t.Fatalf("flag %q missing from %v", flagName, flagNames(flags))
}
if got := fn("--global"); got != want {
t.Errorf("flag %q = %q, want %q", flagName, got, want)
}
}
func flagNames(flags map[string]common.ReportFunc) []string {
names := []string{}
for name := range flags {
names = append(names, name)
}
return names
}

View File

@@ -187,6 +187,7 @@ go-tests:
@$(MAKE) go-test-plugin PLUGIN_NAME=buildpacks @$(MAKE) go-test-plugin PLUGIN_NAME=buildpacks
@$(MAKE) go-test-plugin PLUGIN_NAME=scheduler-k3s @$(MAKE) go-test-plugin PLUGIN_NAME=scheduler-k3s
@$(MAKE) go-test-plugin PLUGIN_NAME=storage @$(MAKE) go-test-plugin PLUGIN_NAME=storage
@$(MAKE) go-test-plugin PLUGIN_NAME=traefik-vhosts
go-test-plugin: go-test-plugin:
cd plugins/$(PLUGIN_NAME) && go get github.com/onsi/gomega && DOKKU_ROOT=/home/dokku DOKKU_LIB_ROOT=/var/lib/dokku go test -v -p 1 -race -mod=readonly || exit $$? cd plugins/$(PLUGIN_NAME) && go get github.com/onsi/gomega && DOKKU_ROOT=/home/dokku DOKKU_LIB_ROOT=/var/lib/dokku go test -v -p 1 -race -mod=readonly || exit $$?

View File

@@ -3,11 +3,14 @@
load test_helper load test_helper
setup() { setup() {
dokku traefik:set --global basic-auth-password
dokku traefik:set --global dns-provider-test_key
create_app create_app
} }
teardown() { teardown() {
destroy_app destroy_app
dokku traefik:set --global basic-auth-password >/dev/null 2>&1 || true
dokku traefik:set --global challenge-mode >/dev/null 2>&1 || true dokku traefik:set --global challenge-mode >/dev/null 2>&1 || true
dokku traefik:set --global dns-provider-test_key >/dev/null 2>&1 || true dokku traefik:set --global dns-provider-test_key >/dev/null 2>&1 || true
} }
@@ -58,15 +61,74 @@ teardown() {
assert_success assert_success
assert_output_contains "*******" assert_output_contains "*******"
run /bin/bash -c "dokku traefik:report --global --traefik-dns-provider-test_key" run /bin/bash -c "dokku traefik:report --global --traefik-global-dns-provider-test_key"
echo "output: $output" echo "output: $output"
echo "status: $status" echo "status: $status"
assert_success assert_success
assert_output "secretvalue" assert_output "secretvalue"
run /bin/bash -c "dokku traefik:report --global --format json | jq -r '.\"dns-provider-test_key\"'" run /bin/bash -c "dokku traefik:report --global --format json | jq -r '.\"global-dns-provider-test_key\"'"
echo "output: $output" echo "output: $output"
echo "status: $status" echo "status: $status"
assert_success assert_success
assert_output "secretvalue" assert_output "secretvalue"
} }
@test "(traefik:report) basic-auth-password is masked unless queried" {
run /bin/bash -c "dokku traefik:report --global --format json | jq -r '.\"global-basic-auth-password\"'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output ""
run /bin/bash -c "dokku traefik:report --global | grep 'basic auth password'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_not_contains "*******"
run /bin/bash -c "dokku traefik:set --global basic-auth-password secretpassword"
assert_success
run /bin/bash -c "dokku traefik:report --global | grep 'basic auth password'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "*******" 2
assert_output_not_contains "secretpassword"
run /bin/bash -c "dokku traefik:report --global --traefik-global-basic-auth-password"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "secretpassword"
run /bin/bash -c "dokku traefik:report --global --traefik-computed-basic-auth-password"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "secretpassword"
run /bin/bash -c "dokku traefik:report --global --format json | jq -r '.\"global-basic-auth-password\"'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "secretpassword"
}
@test "(traefik:report) credentials are masked in the aggregate report" {
run /bin/bash -c "dokku traefik:set --global dns-provider-test_key secretvalue"
assert_success
run /bin/bash -c "dokku traefik:set --global basic-auth-password secretpassword"
assert_success
run /bin/bash -c "dokku report $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "global dns provider test_key"
assert_output_contains "*******" -1
assert_output_not_contains "secretvalue"
assert_output_not_contains "secretpassword"
}

View File

@@ -10,6 +10,8 @@ setup() {
dokku traefik:set --global api-enabled dokku traefik:set --global api-enabled
dokku traefik:set --global api-entry-point dokku traefik:set --global api-entry-point
dokku traefik:set --global api-entry-point-address dokku traefik:set --global api-entry-point-address
dokku traefik:set --global basic-auth-username
dokku traefik:set --global basic-auth-password
dokku traefik:set --global challenge-mode dokku traefik:set --global challenge-mode
dokku traefik:set --global dns-provider dokku traefik:set --global dns-provider
dokku traefik:start dokku traefik:start
@@ -539,19 +541,19 @@ teardown() {
echo "output: $output" echo "output: $output"
echo "status: $status" echo "status: $status"
assert_success assert_success
assert_output_contains "dns provider cf_api_email" assert_output_contains "global dns provider cf_api_email"
assert_output_contains "dns provider cf_api_key" assert_output_contains "global dns provider cf_api_key"
assert_output_contains "*******" 2 assert_output_contains "*******" 2
assert_output_not_contains "test@example.com" assert_output_not_contains "test@example.com"
assert_output_not_contains "secret-key" assert_output_not_contains "secret-key"
run /bin/bash -c "dokku traefik:report $TEST_APP --traefik-dns-provider-cf_api_email" run /bin/bash -c "dokku traefik:report $TEST_APP --traefik-global-dns-provider-cf_api_email"
echo "output: $output" echo "output: $output"
echo "status: $status" echo "status: $status"
assert_success assert_success
assert_output "test@example.com" assert_output "test@example.com"
run /bin/bash -c "dokku traefik:report $TEST_APP --traefik-dns-provider-cf_api_key" run /bin/bash -c "dokku traefik:report $TEST_APP --traefik-global-dns-provider-cf_api_key"
echo "output: $output" echo "output: $output"
echo "status: $status" echo "status: $status"
assert_success assert_success
@@ -571,8 +573,8 @@ teardown() {
echo "output: $output" echo "output: $output"
echo "status: $status" echo "status: $status"
assert_success assert_success
assert_output_not_contains "dns provider cf_api_email" assert_output_not_contains "global dns provider cf_api_email"
assert_output_not_contains "dns provider cf_api_key" assert_output_not_contains "global dns provider cf_api_key"
} }
@test "(traefik) [dns-01] dns-provider-* can only be set globally" { @test "(traefik) [dns-01] dns-provider-* can only be set globally" {
@@ -610,13 +612,13 @@ teardown() {
echo "status: $status" echo "status: $status"
assert_success assert_success
run /bin/bash -c "dokku traefik:report $TEST_APP --format json | jq -r '.\"dns-provider-cf_api_email\"'" run /bin/bash -c "dokku traefik:report $TEST_APP --format json | jq -r '.\"global-dns-provider-cf_api_email\"'"
echo "output: $output" echo "output: $output"
echo "status: $status" echo "status: $status"
assert_success assert_success
assert_output "test@example.com" assert_output "test@example.com"
run /bin/bash -c "dokku traefik:report $TEST_APP --format json | jq -r '.\"dns-provider-cf_api_key\"'" run /bin/bash -c "dokku traefik:report $TEST_APP --format json | jq -r '.\"global-dns-provider-cf_api_key\"'"
echo "output: $output" echo "output: $output"
echo "status: $status" echo "status: $status"
assert_success assert_success