Skip to content

STEP CLI assume human-in-the-loop (ignoring STDIN), making it difficult to script #502

Open
@eengstrom

Description

@eengstrom

The STEP CLI is not easily used in a non-interactive (i.e. scripted) fashion, since it seems to assume a human-in-the-loop.

What would you like to be added

It's not clear what the "best" solution is to the problem, fully described below. I see several possible options:

  • CLI should provide command line option or environment variable equivalents for all TTY prompts,
  • CLI should have a flag, say --non-interactive, never prompting the user and accepting all 'defaults',
  • CLI should accept input on STDIN for all prompts (admittedly brittle).

Why this is needed

As currently implemented, step-cli looks to be coded to open the TTY directly to ask questions of the user, assuming there is a human running the script. Moreover, there is no option to say --non-interactive (i.e. --don't-prompt-me--I'm-not-human), nor is there a way to provide answers to the all the questions that may be asked. This is a problem when trying to build automation around the cli, using Ansible, for example.

One very (POSIX) traditional, albeit brittle, way of providing input to a CLI would be to pipe the answers in via STDIN, in a pipeline fashion. Looking briefly through the code base, it looks like the use of the ui.Prompt() is pervasive, which is great from a consistency perspective, but that function doesn't pay any attention to STDIN, and attempts to directly interact with the underlying TTY.

Another way would be to ensure that all questions have corresponding command line options or environment variable equivalents that can be used if there is no TTY from which to read. Perhaps the most uniform way would be to uniquely "TAG" all the questions being asked. Then a generic -o TAG=value command line option could be added. This would also be amenable to allowing STEPCLI_TAG=value step-cli ... environment variable equivalents. Most of the functionality for this would be implemented directly in ui.go, thus minimizing pervasive changes.

The final approach I can envision, and probably the easiest to implement, would be to add a --non-interactive option to the CLI, which would be used in the ui/ui.go function ui.Prompt() to avoid even the attempt to open the TTY and ask a question, instead returning the default response directly as the answer.

Honestly, that last approach could probably be assumed if there is no TTY available, thus no additional command-line option (e.g. --non-interactive) is needed at all.

Example Use Case

My goal is to automate use of the step-cli. This most recently came up because I am deploying a new CA, replacing an existing CA, and therefore my current Ansible playbooks (i.e. scripts) are trying to issue a command like this:

step-cli ca bootstrap --ca-url=https://HOSTNAME:443 --fingerprint=HASH

If I issue that command by hand, I get a nice prompt:

✗ Would you like to overwrite /root/.step/certs/root_ca.crt [y/n]: █

However, in the case of the automation, this fails with the above no /dev/tty error. Attempts to close stdin or do yes no | step-cli ca ... have no effect, obviously, since the cli doesn't even look at STDIN.

I know I can manually remove the config file or directory before running the command, or use the --force option, but in this case, I want to get an error (return code != 0) if the host thinks it's already got a CA configured, and then have the automation report that to the user, or do something smarter. Ideally, the answer no could be provided to the CLI, in which case I would get exactly what I want, just like as if I run the command by hand:

root@steptest # step-cli ca bootstrap --ca-url=https://HOST:443 --fingerprint=HASH
✔ Would you like to overwrite /root/.step/certs/root_ca.crt [y/n]: n
unexpected error on /root/.step/certs/root_ca.crt: file exists

root@steptest # echo $?
1

However, as it is currently, when running that command via my scripts, this fails with an unhelpful error:

open /dev/tty: no such device or address

Note that the fundamental problem is much bigger than my example here. IMO, anywhere the cli would issue a prompt to a presumed human user should be avoidable by some means employable by scripts/ CI / automation.

Related Issues

I believe versions of this problem have been discussed before, in one or more of these issues/PRs:

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions