【发布时间】:2019-11-09 18:56:43
【问题描述】:
我正在编写一个kubectl 插件来验证用户,我想在插件被调用后提示用户输入密码。据我了解,从 STDIN 获取输入相当简单,但我很难看到写入 STDOUT 的消息。目前我的代码如下所示:
在 cmd/kubectl-myauth.go 中:
// This is mostly boilerplate, but it's needed for the MRE
// https://stackoverflow.com/help/minimal-reproducible-example
package myauth
import (...)
func main() {
pflag.CommandLine = pflag.NewFlagSet("kubectl-myauth", pflag.ExitOnError)
root := cmd.NewCmdAuthOp(genericclioptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr})
if err := root.Execute(); err != nil {
os.Exit(1)
}
}
在 pkg/cmd/auth.go 中:
package cmd
...
type AuthOpOptions struct {
configFlags *genericclioptions.ConfigFlags
resultingContext *api.Context
rawConfig api.Config
args []string
...
genericclioptions.IOStreams
}
func NewAuthOpOptions(streams genericclioptions.IOStreams) *AuthOpOptions {
return &AuthOpOptions{
configFlags: genericclioptions.NewConfigFlags(true),
IOStreams: streams,
}
}
func NewCmdAuthOp(streams genericclioptions.IOStreams) *cobra.Command {
o := NewAuthOpOptions(streams)
cmd := &cobra.Command{
RunE: func(c *cobra.Command, args []string) error {
return o.Run()
},
}
return cmd
}
func (o *AuthOpOptions) Run() error {
pass, err := getPassword(o)
if err != nil {
return err
}
// Do Auth Stuff
// Eventually print an ExecCredential to STDOUT
return nil
}
func getPassword(o *AuthOpOptions) (string, error) {
var reader *bufio.Reader
reader = nil
pass := ""
for pass == "" {
// THIS IS AN IMPORTANT LINE [1]
fmt.Fprintf(o.IOStreams.Out, "Password with which to authenticate:\n")
// THE REST OF THIS IS STILL IMPORTANT, BUT LESS SO [2]
if reader == nil {
// The first time through, initialize the reader
reader = bufio.NewReader(o.IOStreams.In)
}
pass, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
pass = strings.Trim(pass, "\r\n")
if pass == "" {
// ALSO THIS LINE IS IMPORTANT [3]
fmt.Fprintf(o.IOStreams.Out, `Read password was empty string.
Please input a valid password.
`)
}
}
return pass, nil
}
这符合我在kubectl 上下文之外运行时所期望的方式——即打印字符串、提示输入并继续。但是,在kubectl 上下文中,我相信前两个全大写 cmets([1] 和 [2])之间的打印被kubectl 在 STDOUT 上监听。我可以通过打印到 STDERR 来解决这个问题,但这感觉......错了。有没有办法绕过kubectl的STDOUT消耗与用户交流?
TL;DR:kubectl 似乎正在吞噬 kubectl 插件的所有 STDOUT,但我想提示用户输入 - 有没有简单的方法可以做到这一点?
【问题讨论】:
-
我想指出,STDERR 在这里不一定被视为错误。考虑一下:您正在使用一种工具,用户可以在其中处理输出 (STDOUT)。他们可能将输出传送到脚本或从脚本传送。如果您的提示信息被发送到 STDOUT,它不仅会被脚本/管道吞噬,还可能导致下游脚本问题。当在 STDERR 上出现提示时,它允许用户查看它,输入所需的信息,并让命令在脚本/管道中继续,因为它通常在流中没有额外信息。
-
我自称对“用户提示使用哪种输出机制”一无所知 - STDERR 在这里可能是正确的。这让我很好奇为什么许多命令行工具的设计者不提供四种输出机制 - 输出给用户(或上一级),错误输出给用户(或上一级),输出到我,对我犯错。这似乎更...开发人员友好?呃,业余PR目标。
-
@distortedsignal Andy 的评论是否令人满意,可以将其视为您问题的答案?
-
@OhHiMark - 不是吗? Andy 的回答是一个很好的解决方法,但是“要求输入密码是一个错误”并不是一个很好的答案(因为在我的示例中,它在预期的工作流程中)。这有意义吗?
-
@distortedsignal 谢谢!我已经重现了这个问题。我会试一试的。
标签: go kubernetes kubectl