diff --git a/README.md b/README.md index 3862e0b..8b3de95 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,9 @@ and `help` are the subcommands; every other first argument is a common name. A bare invocation prints help and exits zero only when stdout is a terminal — a pipe, a file, a socket, `/dev/null` and a closed fd 1 all exit non-zero with -nothing on stdout. Puppet's `generate()` reads stdout through a pipe, so there -a missing common name fails the compile rather than feeding help text into the -catalog. +nothing on stdout. Puppet's `generate()` collects stdout through a temp file, +so there a missing common name fails the compile rather than feeding help text +into the catalog. | Flag | Short | Type | Default | Description | |---|---|---|---|---| diff --git a/cmd/certmanager/main.go b/cmd/certmanager/main.go index bec4cc1..f196a22 100644 --- a/cmd/certmanager/main.go +++ b/cmd/certmanager/main.go @@ -43,8 +43,8 @@ func runWith(args []string, stdout, stderr io.Writer, interactive bool) int { return 0 } -// isTerminal reports whether w is a terminal. Puppet's generate() captures -// stdout through a pipe, so anything else is a machine caller. The check is a +// isTerminal reports whether w is a terminal. Puppet hands the child a temp +// file as fd 1, never a terminal, so anything else is a machine caller. The check is a // terminal ioctl, not a character-device stat: /dev/null is a character device, // and so is the /dev/null the Go runtime reopens onto a closed fd 1 at startup. func isTerminal(w io.Writer) bool { diff --git a/cmd/certmanager/main_test.go b/cmd/certmanager/main_test.go index 4dfd50f..d76d58f 100644 --- a/cmd/certmanager/main_test.go +++ b/cmd/certmanager/main_test.go @@ -869,10 +869,18 @@ func TestIsTerminalRejectsNonTerminals(t *testing.T) { if isTerminal(f) { t.Errorf("%s is not a terminal", name) } + // An explicit empty argv, never nil: cobra falls back to os.Args[1:] + // for a nil one, which under `go test -args foo` would turn this into + // `certmanager foo` and issue against whatever Vault the host's + // config resolves to. The stderr assertion is what catches that. var stderr bytes.Buffer - if code := run(nil, f, &stderr); code == 0 { + code := run([]string{}, f, &stderr) + if code == 0 { t.Errorf("%s: bare invocation exit code = 0, want non-zero", name) } + if want := "a common name is required"; !strings.Contains(stderr.String(), want) { + t.Errorf("%s: stderr = %q, want it to contain %q", name, stderr.String(), want) + } } }