在 kubectl 插件中,提示输入?
Inside kubectl plugin, prompt for input?
我正在编写一个 kubectl
插件来验证用户身份,我想在调用插件后提示用户输入密码。据我了解,从 STDIN 获取输入相当简单,但我很难看到写入 STDOUT 的消息。目前我的代码如下所示:
在cmd/kubectl-myauth.go:
// This is mostly boilerplate, but it's needed for the MRE
// https://whosebug.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
上下文中,我相信前两个全大写注释([1] 和 [2])之间的打印被 kubectl
监听的 STDOUT 吞没了。我可以通过打印到 STDERR 来解决这个问题,但这感觉……不对。有什么方法可以绕过 kubectl
使用 STDOUT 与用户通信?
TL;DR: kubectl
似乎吞噬了 kubectl
插件的所有 STDOUT,但我想提示用户输入 - 有没有简单的方法可以做到这一点?
抱歉,我没有比“对我有用”更好的答案了:-)步骤如下:
git clone https://github.com/kubernetes/kubernetes.git
将 sample-cli-plugin
复制为 test-cli-plugin
(这涉及修复导入-restrictions.yaml、规则-godeps.yaml 和 rules.yaml 下的 staging/publishing - 也许没有必要,但这样更安全)
将 kubectl-ns.go 更改为 kubectl-test.go:
package main
import (
"os"
"github.com/spf13/pflag"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/test-cli-plugin/pkg/cmd"
)
func main() {
flags := pflag.NewFlagSet("kubectl-test", pflag.ExitOnError)
pflag.CommandLine = flags
root := cmd.NewCmdTest(genericclioptions.IOStreams{In: os.Stdin,
Out: os.Stdout,
ErrOut: os.Stderr})
if err := root.Execute(); err != nil {
os.Exit(1)
}
}
- 将 ns.go 更改为 test.go:
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
type TestOptions struct {
configFlags *genericclioptions.ConfigFlags
genericclioptions.IOStreams
}
func NewTestOptions(streams genericclioptions.IOStreams) *TestOptions {
return &TestOptions{
configFlags: genericclioptions.NewConfigFlags(true),
IOStreams: streams,
}
}
func NewCmdTest(streams genericclioptions.IOStreams) *cobra.Command {
o := NewTestOptions(streams)
cmd := &cobra.Command{
Use: "test",
Short: "Test plugin",
SilenceUsage: true,
RunE: func(c *cobra.Command, args []string) error {
o.Run()
return nil
},
}
return cmd
}
func (o *TestOptions) Run() error {
fmt.Fprintf(os.Stderr, "Testing Fprintf Stderr\n")
fmt.Fprintf(os.Stdout, "Testing Fprintf Stdout\n")
fmt.Printf("Testing Printf\n")
fmt.Fprintf(o.IOStreams.Out, "Testing Fprintf o.IOStreams.Out\n")
return nil
}
- 相应地修复 BUILD 文件
- 构建插件
- 运行
make
- 复制
kubectl-test
到/usr/local/bin
- 运行 已编译的
kubectl
二进制文件:
~/k8s/_output/bin$ ./kubectl test
Testing Fprintf Stderr
Testing Fprintf Stdout
Testing Printf
Testing Fprintf o.IOStreams.Out
我正在编写一个 kubectl
插件来验证用户身份,我想在调用插件后提示用户输入密码。据我了解,从 STDIN 获取输入相当简单,但我很难看到写入 STDOUT 的消息。目前我的代码如下所示:
在cmd/kubectl-myauth.go:
// This is mostly boilerplate, but it's needed for the MRE
// https://whosebug.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
上下文中,我相信前两个全大写注释([1] 和 [2])之间的打印被 kubectl
监听的 STDOUT 吞没了。我可以通过打印到 STDERR 来解决这个问题,但这感觉……不对。有什么方法可以绕过 kubectl
使用 STDOUT 与用户通信?
TL;DR: kubectl
似乎吞噬了 kubectl
插件的所有 STDOUT,但我想提示用户输入 - 有没有简单的方法可以做到这一点?
抱歉,我没有比“对我有用”更好的答案了:-)步骤如下:
git clone https://github.com/kubernetes/kubernetes.git
将
sample-cli-plugin
复制为test-cli-plugin
(这涉及修复导入-restrictions.yaml、规则-godeps.yaml 和 rules.yaml 下的 staging/publishing - 也许没有必要,但这样更安全)将 kubectl-ns.go 更改为 kubectl-test.go:
package main
import (
"os"
"github.com/spf13/pflag"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/test-cli-plugin/pkg/cmd"
)
func main() {
flags := pflag.NewFlagSet("kubectl-test", pflag.ExitOnError)
pflag.CommandLine = flags
root := cmd.NewCmdTest(genericclioptions.IOStreams{In: os.Stdin,
Out: os.Stdout,
ErrOut: os.Stderr})
if err := root.Execute(); err != nil {
os.Exit(1)
}
}
- 将 ns.go 更改为 test.go:
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
type TestOptions struct {
configFlags *genericclioptions.ConfigFlags
genericclioptions.IOStreams
}
func NewTestOptions(streams genericclioptions.IOStreams) *TestOptions {
return &TestOptions{
configFlags: genericclioptions.NewConfigFlags(true),
IOStreams: streams,
}
}
func NewCmdTest(streams genericclioptions.IOStreams) *cobra.Command {
o := NewTestOptions(streams)
cmd := &cobra.Command{
Use: "test",
Short: "Test plugin",
SilenceUsage: true,
RunE: func(c *cobra.Command, args []string) error {
o.Run()
return nil
},
}
return cmd
}
func (o *TestOptions) Run() error {
fmt.Fprintf(os.Stderr, "Testing Fprintf Stderr\n")
fmt.Fprintf(os.Stdout, "Testing Fprintf Stdout\n")
fmt.Printf("Testing Printf\n")
fmt.Fprintf(o.IOStreams.Out, "Testing Fprintf o.IOStreams.Out\n")
return nil
}
- 相应地修复 BUILD 文件
- 构建插件
- 运行
make
- 复制
kubectl-test
到/usr/local/bin - 运行 已编译的
kubectl
二进制文件:
~/k8s/_output/bin$ ./kubectl test
Testing Fprintf Stderr
Testing Fprintf Stdout
Testing Printf
Testing Fprintf o.IOStreams.Out