douwen0612 2019-06-27 17:51
浏览 66

在kubectl插件中,提示输入?

I'm writing a kubectl plugin to authenticate users, and I would like to prompt the user for a password after the plugin is invoked. From what I understand, it's fairly trivial to get input from STDIN, but I'm struggling seeing messages written to STDOUT. Currently my code looks like this:

In 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)
    }
}

In 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:
")
        // 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('
')
        if err != nil {
            return nil, err
        }
        pass = strings.Trim(pass, "
")
        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
}

This works the way that I expect when running from outside of the kubectl context - namely, it prints the string, prompts for input, and continues. However, from inside the kubectl context, I believe the print between the first two all-caps comments ([1] and [2]) is being swallowed by kubectl listening on STDOUT. I can get around this by printing to STDERR, but that feels... wrong. Is there a way that I can bypass kubectl's consumption of STDOUT to communicate with the user?

TL;DR: kubectl appears to be swallowing all of STDOUT for kubectl plugins, but I want to prompt the user for input - is there a simple way to do this?

  • 写回答

1条回答 默认 最新

  • duanhao4156 2019-09-16 15:26
    关注

    Sorry I have no better answer than "Works for me" :-) Here are the steps:

    • git clone https://github.com/kubernetes/kubernetes.git

    • duplicate sample-cli-plugin as test-cli-plugin (this involves fixing import-restrictions.yaml, rules-godeps.yaml and rules.yaml under staging/publishing - maybe not necessary, but it's safer this way)

    • change kubectl-ns.go to 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)
            }
    }
    
    • change ns.go to 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
    ")
            fmt.Fprintf(os.Stdout, "Testing Fprintf Stdout
    ")
            fmt.Printf("Testing Printf
    ")
            fmt.Fprintf(o.IOStreams.Out, "Testing Fprintf o.IOStreams.Out
    ")
            return nil
    }
    
    • fix BUILD files accordingly
    • build the plugin
    • run make
    • copy kubectl-test to /usr/local/bin
    • run the compiled kubectl binary:

    ~/k8s/_output/bin$ ./kubectl test

    Testing Fprintf Stderr

    Testing Fprintf Stdout

    Testing Printf

    Testing Fprintf o.IOStreams.Out

    评论

报告相同问题?

悬赏问题

  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度
  • ¥30 关于#r语言#的问题:如何对R语言中mfgarch包中构建的garch-midas模型进行样本内长期波动率预测和样本外长期波动率预测
  • ¥15 ETLCloud 处理json多层级问题
  • ¥15 matlab中使用gurobi时报错
  • ¥15 这个主板怎么能扩出一两个sata口
  • ¥15 不是,这到底错哪儿了😭
  • ¥15 2020长安杯与连接网探
  • ¥15 关于#matlab#的问题:在模糊控制器中选出线路信息,在simulink中根据线路信息生成速度时间目标曲线(初速度为20m/s,15秒后减为0的速度时间图像)我想问线路信息是什么