<p>I'm writing a <code>kubectl</code> 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:</p>

<p>In cmd/kubectl-myauth.go:</p>

<pre class="lang-golang prettyprint-override"><code>// 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)
    }
}
</code></pre>

<p>In pkg/cmd/auth.go:</p>

<pre class="lang-golang prettyprint-override"><code>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 &amp;AuthOpOptions{
        configFlags: genericclioptions.NewConfigFlags(true),
        IOStreams: streams,
    }
}
func NewCmdAuthOp(streams genericclioptions.IOStreams) *cobra.Command {
    o := NewAuthOpOptions(streams)
    cmd := &amp;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
}
</code></pre>

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

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

distortedsignal
<p>Sorry I have no better answer than &quot;Works for me&quot; :-) Here are the steps:</p>
<ul>
<li><p><code>git clone https://github.com/kubernetes/kubernetes.git</code></p>
</li>
<li><p>duplicate <code>sample-cli-plugin</code> as <code>test-cli-plugin</code> (this involves fixing import-restrictions.yaml, rules-godeps.yaml and rules.yaml under staging/publishing - maybe not necessary, but it's safer this way)</p>
</li>
<li><p>change kubectl-ns.go to kubectl-test.go:</p>
</li>
</ul>
<pre class="lang-golang prettyprint-override"><code>package main

import (
        &quot;os&quot;

        &quot;github.com/spf13/pflag&quot;

        &quot;k8s.io/cli-runtime/pkg/genericclioptions&quot;
        &quot;k8s.io/test-cli-plugin/pkg/cmd&quot;
)

func main() {
        flags := pflag.NewFlagSet(&quot;kubectl-test&quot;, 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)
        }
}
</code></pre>
<ul>
<li>change ns.go to test.go:</li>
</ul>
<pre class="lang-golang prettyprint-override"><code>package cmd

import (
        &quot;fmt&quot;
        &quot;os&quot;

        &quot;github.com/spf13/cobra&quot;

        &quot;k8s.io/cli-runtime/pkg/genericclioptions&quot;
)

type TestOptions struct {
        configFlags *genericclioptions.ConfigFlags
        genericclioptions.IOStreams
}

func NewTestOptions(streams genericclioptions.IOStreams) *TestOptions {
        return &amp;TestOptions{
                configFlags: genericclioptions.NewConfigFlags(true),
                IOStreams:   streams,
        }
}

func NewCmdTest(streams genericclioptions.IOStreams) *cobra.Command {
        o := NewTestOptions(streams)

        cmd := &amp;cobra.Command{
                Use:          &quot;test&quot;,
                Short:        &quot;Test plugin&quot;,
                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, &quot;Testing Fprintf Stderr\n&quot;)
        fmt.Fprintf(os.Stdout, &quot;Testing Fprintf Stdout\n&quot;)
        fmt.Printf(&quot;Testing Printf\n&quot;)
        fmt.Fprintf(o.IOStreams.Out, &quot;Testing Fprintf o.IOStreams.Out\n&quot;)
        return nil
}
</code></pre>
<ul>
<li>fix BUILD files accordingly</li>
<li>build the plugin</li>
<li>run <code>make</code></li>
<li>copy <code>kubectl-test</code> to /usr/local/bin</li>
<li>run the compiled <code>kubectl</code> binary:</li>
</ul>
<blockquote>
<p>~/k8s/_output/bin$ ./kubectl test</p>
<p>Testing Fprintf Stderr</p>
<p>Testing Fprintf Stdout</p>
<p>Testing Printf</p>
<p>Testing Fprintf o.IOStreams.Out</p>
</blockquote>
