use IOStreams for cli commands

pull/8/head
David Eads 2018-05-08 09:02:34 -04:00
parent 8d064823bb
commit facd04be43
33 changed files with 295 additions and 296 deletions

View File

@ -17,17 +17,16 @@ limitations under the License.
package cmd
import (
"io"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
// NewCmdAlpha creates a command that acts as an alternate root command for features in alpha
func NewCmdAlpha(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cobra.Command {
func NewCmdAlpha(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "alpha",
Short: i18n.T("Commands for features in alpha"),
@ -37,7 +36,7 @@ func NewCmdAlpha(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cobra.Com
// Alpha commands should be added here. As features graduate from alpha they should move
// from here to the CommandGroups defined by NewKubeletCommand() in cmd.go.
//cmd.AddCommand(NewCmdDebug(f, in, out, err))
cmd.AddCommand(NewCmdDiff(f, out, err))
cmd.AddCommand(NewCmdDiff(f, streams))
// NewKubeletCommand() will hide the alpha command if it has no subcommands. Overriding
// the help function ensures a reasonable message if someone types the hidden command anyway.

View File

@ -197,7 +197,7 @@ func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
return err
}
o.DeleteOptions = o.DeleteFlags.ToOptions(o.Out, o.ErrOut)
o.DeleteOptions = o.DeleteFlags.ToOptions(o.IOStreams)
return nil
}

View File

@ -35,6 +35,7 @@ import (
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
@ -60,12 +61,10 @@ const (
defaultPodLogsTimeout = 20 * time.Second
)
func NewCmdAttach(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command {
options := &AttachOptions{
func NewCmdAttach(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
o := &AttachOptions{
StreamOptions: StreamOptions{
In: cmdIn,
Out: cmdOut,
Err: cmdErr,
IOStreams: streams,
},
Attach: &DefaultRemoteAttach{},
@ -77,15 +76,15 @@ func NewCmdAttach(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer)
Long: "Attach to a process that is already running inside an existing container.",
Example: attachExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.Validate())
cmdutil.CheckErr(options.Run())
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodAttachTimeout)
cmd.Flags().StringVarP(&options.ContainerName, "container", "c", options.ContainerName, "Container name. If omitted, the first container in the pod will be chosen")
cmd.Flags().BoolVarP(&options.Stdin, "stdin", "i", options.Stdin, "Pass stdin to the container")
cmd.Flags().BoolVarP(&options.TTY, "tty", "t", options.TTY, "Stdin is a TTY")
cmd.Flags().StringVarP(&o.ContainerName, "container", "c", o.ContainerName, "Container name. If omitted, the first container in the pod will be chosen")
cmd.Flags().BoolVarP(&o.Stdin, "stdin", "i", o.Stdin, "Pass stdin to the container")
cmd.Flags().BoolVarP(&o.TTY, "tty", "t", o.TTY, "Stdin is a TTY")
return cmd
}
@ -203,7 +202,7 @@ func (p *AttachOptions) Validate() error {
if len(p.PodName) == 0 {
allErrs = append(allErrs, errors.New("pod name must be specified"))
}
if p.Out == nil || p.Err == nil {
if p.Out == nil || p.ErrOut == nil {
allErrs = append(allErrs, errors.New("both output and error output must be provided"))
}
if p.Attach == nil || p.PodClient == nil || p.Config == nil {
@ -236,8 +235,8 @@ func (p *AttachOptions) Run() error {
}
if p.TTY && !containerToAttach.TTY {
p.TTY = false
if p.Err != nil {
fmt.Fprintf(p.Err, "Unable to use a TTY - container %s did not allocate one\n", containerToAttach.Name)
if p.ErrOut != nil {
fmt.Fprintf(p.ErrOut, "Unable to use a TTY - container %s did not allocate one\n", containerToAttach.Name)
}
} else if !p.TTY && containerToAttach.TTY {
// the container was launched with a TTY, so we have to force a TTY here, otherwise you'll get
@ -249,7 +248,7 @@ func (p *AttachOptions) Run() error {
t := p.setupTTY()
// save p.Err so we can print the command prompt message below
stderr := p.Err
stderr := p.ErrOut
var sizeQueue remotecommand.TerminalSizeQueue
if t.Raw {
@ -266,7 +265,7 @@ func (p *AttachOptions) Run() error {
// unset p.Err if it was previously set because both stdout and stderr go over p.Out when tty is
// true
p.Err = nil
p.ErrOut = nil
}
fn := func() error {
@ -284,11 +283,11 @@ func (p *AttachOptions) Run() error {
Container: containerToAttach.Name,
Stdin: p.Stdin,
Stdout: p.Out != nil,
Stderr: p.Err != nil,
Stderr: p.ErrOut != nil,
TTY: t.Raw,
}, legacyscheme.ParameterCodec)
return p.Attach.Attach("POST", req.URL(), p.Config, p.In, p.Out, p.Err, t.Raw, sizeQueue)
return p.Attach.Attach("POST", req.URL(), p.Config, p.In, p.Out, p.ErrOut, t.Raw, sizeQueue)
}
if !p.Quiet && stderr != nil {
@ -322,8 +321,8 @@ func (p *AttachOptions) containerToAttachTo(pod *api.Pod) (*api.Container, error
}
if len(p.SuggestedCmdUsage) > 0 {
fmt.Fprintf(p.Err, "Defaulting container name to %s.\n", pod.Spec.Containers[0].Name)
fmt.Fprintf(p.Err, "%s\n", p.SuggestedCmdUsage)
fmt.Fprintf(p.ErrOut, "Defaulting container name to %s.\n", pod.Spec.Containers[0].Name)
fmt.Fprintf(p.ErrOut, "%s\n", p.SuggestedCmdUsage)
}
glog.V(4).Infof("defaulting container name to %s", pod.Spec.Containers[0].Name)

View File

@ -17,7 +17,6 @@ limitations under the License.
package cmd
import (
"bytes"
"fmt"
"io"
"net/http"
@ -38,6 +37,7 @@ import (
api "k8s.io/kubernetes/pkg/apis/core"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
@ -249,9 +249,6 @@ func TestAttach(t *testing.T) {
}
tf.Namespace = "test"
tf.ClientConfigVal = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs, GroupVersion: &schema.GroupVersion{Version: test.version}}}
bufOut := bytes.NewBuffer([]byte{})
bufErr := bytes.NewBuffer([]byte{})
bufIn := bytes.NewBuffer([]byte{})
remoteAttach := &fakeRemoteAttach{}
if test.remoteAttachErr {
remoteAttach.err = fmt.Errorf("attach error")
@ -259,9 +256,7 @@ func TestAttach(t *testing.T) {
params := &AttachOptions{
StreamOptions: StreamOptions{
ContainerName: test.container,
In: bufIn,
Out: bufOut,
Err: bufErr,
IOStreams: genericclioptions.NewTestIOStreamsDiscard(),
},
Attach: remoteAttach,
GetPodTimeout: 1000,
@ -342,16 +337,12 @@ func TestAttachWarnings(t *testing.T) {
}
tf.Namespace = "test"
tf.ClientConfigVal = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs, GroupVersion: &schema.GroupVersion{Version: test.version}}}
bufOut := bytes.NewBuffer([]byte{})
bufErr := bytes.NewBuffer([]byte{})
bufIn := bytes.NewBuffer([]byte{})
streams, _, _, bufErr := genericclioptions.NewTestIOStreams()
ex := &fakeRemoteAttach{}
params := &AttachOptions{
StreamOptions: StreamOptions{
ContainerName: test.container,
In: bufIn,
Out: bufOut,
Err: bufErr,
IOStreams: streams,
Stdin: test.stdin,
TTY: test.tty,
},

View File

@ -275,7 +275,7 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
NewCmdExplain("kubectl", f, ioStreams),
get.NewCmdGet("kubectl", f, ioStreams),
NewCmdEdit(f, ioStreams),
NewCmdDelete(f, out, err),
NewCmdDelete(f, ioStreams),
},
},
{
@ -292,7 +292,7 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
Commands: []*cobra.Command{
NewCmdCertificate(f, ioStreams),
NewCmdClusterInfo(f, ioStreams),
NewCmdTop(f, out, err),
NewCmdTop(f, ioStreams),
NewCmdCordon(f, ioStreams),
NewCmdUncordon(f, ioStreams),
NewCmdDrain(f, ioStreams),
@ -303,11 +303,11 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
Message: "Troubleshooting and Debugging Commands:",
Commands: []*cobra.Command{
NewCmdDescribe("kubectl", f, ioStreams),
NewCmdLogs(f, out, err),
NewCmdAttach(f, in, out, err),
NewCmdExec(f, in, out, err),
NewCmdPortForward(f, out, err),
NewCmdProxy(f, out),
NewCmdLogs(f, ioStreams),
NewCmdAttach(f, ioStreams),
NewCmdExec(f, ioStreams),
NewCmdPortForward(f, ioStreams),
NewCmdProxy(f, ioStreams),
NewCmdCp(f, ioStreams),
auth.NewCmdAuth(f, ioStreams),
},
@ -317,7 +317,7 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
Commands: []*cobra.Command{
NewCmdApply("kubectl", f, ioStreams),
NewCmdPatch(f, ioStreams),
NewCmdReplace(f, out, err),
NewCmdReplace(f, ioStreams),
NewCmdConvert(f, ioStreams),
},
},
@ -326,7 +326,7 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
Commands: []*cobra.Command{
NewCmdLabel(f, ioStreams),
NewCmdAnnotate("kubectl", f, ioStreams),
NewCmdCompletion(out, ""),
NewCmdCompletion(ioStreams.Out, ""),
},
},
}
@ -335,7 +335,7 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
filters := []string{"options"}
// Hide the "alpha" subcommand if there are no alpha commands in this build.
alpha := NewCmdAlpha(f, in, out, err)
alpha := NewCmdAlpha(f, ioStreams)
if !alpha.HasSubCommands() {
filters = append(filters, alpha.Name())
}
@ -356,11 +356,11 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
cmds.AddCommand(alpha)
cmds.AddCommand(cmdconfig.NewCmdConfig(f, clientcmd.NewDefaultPathOptions(), ioStreams))
cmds.AddCommand(NewCmdPlugin(f, in, out, err))
cmds.AddCommand(NewCmdPlugin(f, ioStreams))
cmds.AddCommand(NewCmdVersion(f, ioStreams))
cmds.AddCommand(NewCmdApiVersions(f, ioStreams))
cmds.AddCommand(NewCmdApiResources(f, ioStreams))
cmds.AddCommand(NewCmdOptions(out))
cmds.AddCommand(NewCmdOptions(ioStreams.Out))
return cmds
}

View File

@ -18,7 +18,6 @@ package cmd
import (
"archive/tar"
"bytes"
"errors"
"fmt"
"io"
@ -35,6 +34,8 @@ import (
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"bytes"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
)
@ -194,8 +195,10 @@ func (o *CopyOptions) Run(args []string) error {
func (o *CopyOptions) checkDestinationIsDir(dest fileSpec) error {
options := &ExecOptions{
StreamOptions: StreamOptions{
Out: bytes.NewBuffer([]byte{}),
Err: bytes.NewBuffer([]byte{}),
IOStreams: genericclioptions.IOStreams{
Out: bytes.NewBuffer([]byte{}),
ErrOut: bytes.NewBuffer([]byte{}),
},
Namespace: dest.PodNamespace,
PodName: dest.PodName,
@ -240,9 +243,11 @@ func (o *CopyOptions) copyToPod(src, dest fileSpec) error {
options := &ExecOptions{
StreamOptions: StreamOptions{
In: reader,
Out: o.Out,
Err: o.ErrOut,
IOStreams: genericclioptions.IOStreams{
In: reader,
Out: o.Out,
ErrOut: o.ErrOut,
},
Stdin: true,
Namespace: dest.PodNamespace,
@ -263,9 +268,11 @@ func (o *CopyOptions) copyFromPod(src, dest fileSpec) error {
reader, outStream := io.Pipe()
options := &ExecOptions{
StreamOptions: StreamOptions{
In: nil,
Out: outStream,
Err: o.Out,
IOStreams: genericclioptions.IOStreams{
In: nil,
Out: outStream,
ErrOut: o.Out,
},
Namespace: src.PodNamespace,
PodName: src.PodName,

View File

@ -18,7 +18,6 @@ package cmd
import (
"fmt"
"io"
"strings"
"time"
@ -31,6 +30,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
@ -109,11 +109,10 @@ type DeleteOptions struct {
Mapper meta.RESTMapper
Result *resource.Result
Out io.Writer
ErrOut io.Writer
genericclioptions.IOStreams
}
func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
func NewCmdDelete(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
deleteFlags := NewDeleteCommandFlags("containing the resource to delete.")
cmd := &cobra.Command{
@ -123,14 +122,14 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
Long: delete_long,
Example: delete_example,
Run: func(cmd *cobra.Command, args []string) {
options := deleteFlags.ToOptions(out, errOut)
if err := options.Complete(f, out, errOut, args, cmd); err != nil {
o := deleteFlags.ToOptions(streams)
if err := o.Complete(f, args, cmd); err != nil {
cmdutil.CheckErr(err)
}
if err := options.Validate(cmd); err != nil {
if err := o.Validate(cmd); err != nil {
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, err.Error()))
}
if err := options.RunDelete(); err != nil {
if err := o.RunDelete(); err != nil {
cmdutil.CheckErr(err)
}
},
@ -143,7 +142,7 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
return cmd
}
func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args []string, cmd *cobra.Command) error {
func (o *DeleteOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Command) error {
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
@ -175,10 +174,6 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args
return err
}
// Set up writer
o.Out = out
o.ErrOut = errOut
return nil
}

View File

@ -17,12 +17,12 @@ limitations under the License.
package cmd
import (
"io"
"time"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
@ -72,10 +72,9 @@ type DeleteFlags struct {
Output *string
}
func (f *DeleteFlags) ToOptions(out, errOut io.Writer) *DeleteOptions {
func (f *DeleteFlags) ToOptions(streams genericclioptions.IOStreams) *DeleteOptions {
options := &DeleteOptions{
Out: out,
ErrOut: errOut,
IOStreams: streams,
}
// add filename options

View File

@ -17,7 +17,6 @@ limitations under the License.
package cmd
import (
"bytes"
"encoding/json"
"io"
"io/ioutil"
@ -38,6 +37,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
@ -84,8 +84,8 @@ func TestDeleteObjectByTuple(t *testing.T) {
}
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd := NewCmdDelete(tf, buf, errBuf)
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdDelete(tf, streams)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -95,8 +95,8 @@ func TestDeleteObjectByTuple(t *testing.T) {
}
// Test cascading delete of object without client-side reaper doesn't make GET requests
buf, errBuf = bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd = NewCmdDelete(tf, buf, errBuf)
streams, _, buf, _ = genericclioptions.NewTestIOStreams()
cmd = NewCmdDelete(tf, streams)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{"secrets/mysecret"})
@ -147,8 +147,8 @@ func TestOrphanDependentsInDeleteObject(t *testing.T) {
// DeleteOptions.OrphanDependents should be false, when cascade is true (default).
falseVar := false
expectedOrphanDependents = &falseVar
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd := NewCmdDelete(tf, buf, errBuf)
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdDelete(tf, streams)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{"secrets/mysecret"})
@ -159,8 +159,8 @@ func TestOrphanDependentsInDeleteObject(t *testing.T) {
// Test that delete options should be set to orphan when cascade is false.
trueVar := true
expectedOrphanDependents = &trueVar
buf, errBuf = bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd = NewCmdDelete(tf, buf, errBuf)
streams, _, buf, _ = genericclioptions.NewTestIOStreams()
cmd = NewCmdDelete(tf, streams)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -202,8 +202,8 @@ func TestDeleteNamedObject(t *testing.T) {
}
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd := NewCmdDelete(tf, buf, errBuf)
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdDelete(tf, streams)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -213,8 +213,8 @@ func TestDeleteNamedObject(t *testing.T) {
}
// Test cascading delete of object without client-side reaper doesn't make GET requests
buf, errBuf = bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd = NewCmdDelete(tf, buf, errBuf)
streams, _, buf, _ = genericclioptions.NewTestIOStreams()
cmd = NewCmdDelete(tf, streams)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -246,9 +246,9 @@ func TestDeleteObject(t *testing.T) {
}),
}
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd := NewCmdDelete(tf, buf, errBuf)
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdDelete(tf, streams)
cmd.Flags().Set("filename", "../../../test/e2e/testing-manifests/guestbook/legacy/redis-master-controller.yaml")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -318,11 +318,11 @@ func TestDeleteObjectGraceZero(t *testing.T) {
}),
}
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
reaper := &fakeReaper{}
fake := &fakeReaperFactory{Factory: tf, reaper: reaper}
cmd := NewCmdDelete(fake, buf, errBuf)
streams, _, buf, errBuf := genericclioptions.NewTestIOStreams()
cmd := NewCmdDelete(fake, streams)
cmd.Flags().Set("output", "name")
cmd.Flags().Set("grace-period", "0")
cmd.Run(cmd, []string{"pods/nginx"})
@ -357,7 +357,6 @@ func TestDeleteObjectNotFound(t *testing.T) {
}),
}
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
options := &DeleteOptions{
FilenameOptions: resource.FilenameOptions{
@ -366,8 +365,9 @@ func TestDeleteObjectNotFound(t *testing.T) {
GracePeriod: -1,
Cascade: false,
Output: "name",
IOStreams: genericclioptions.NewTestIOStreamsDiscard(),
}
err := options.Complete(tf, buf, errBuf, []string{}, fakecmd())
err := options.Complete(tf, []string{}, fakecmd())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -395,9 +395,9 @@ func TestDeleteObjectIgnoreNotFound(t *testing.T) {
}),
}
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdDelete(tf, buf, errBuf)
cmd := NewCmdDelete(tf, streams)
cmd.Flags().Set("filename", "../../../test/e2e/testing-manifests/guestbook/legacy/redis-master-controller.yaml")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("ignore-not-found", "true")
@ -438,7 +438,6 @@ func TestDeleteAllNotFound(t *testing.T) {
}),
}
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
// Make sure we can explicitly choose to fail on NotFound errors, even with --all
options := &DeleteOptions{
@ -448,8 +447,9 @@ func TestDeleteAllNotFound(t *testing.T) {
DeleteAll: true,
IgnoreNotFound: false,
Output: "name",
IOStreams: genericclioptions.NewTestIOStreamsDiscard(),
}
err := options.Complete(tf, buf, errBuf, []string{"services"}, fakecmd())
err := options.Complete(tf, []string{"services"}, fakecmd())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -489,9 +489,9 @@ func TestDeleteAllIgnoreNotFound(t *testing.T) {
}),
}
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdDelete(tf, buf, errBuf)
cmd := NewCmdDelete(tf, streams)
cmd.Flags().Set("all", "true")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -526,9 +526,9 @@ func TestDeleteMultipleObject(t *testing.T) {
}),
}
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdDelete(tf, buf, errBuf)
cmd := NewCmdDelete(tf, streams)
cmd.Flags().Set("filename", "../../../test/e2e/testing-manifests/guestbook/legacy/redis-master-controller.yaml")
cmd.Flags().Set("filename", "../../../test/e2e/testing-manifests/guestbook/frontend-service.yaml")
cmd.Flags().Set("cascade", "false")
@ -564,7 +564,7 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) {
}),
}
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
options := &DeleteOptions{
FilenameOptions: resource.FilenameOptions{
@ -573,8 +573,9 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) {
GracePeriod: -1,
Cascade: false,
Output: "name",
IOStreams: streams,
}
err := options.Complete(tf, buf, errBuf, []string{}, fakecmd())
err := options.Complete(tf, []string{}, fakecmd())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -616,9 +617,9 @@ func TestDeleteMultipleResourcesWithTheSameName(t *testing.T) {
}),
}
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdDelete(tf, buf, errBuf)
cmd := NewCmdDelete(tf, streams)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -650,9 +651,9 @@ func TestDeleteDirectory(t *testing.T) {
}),
}
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdDelete(tf, buf, errBuf)
cmd := NewCmdDelete(tf, streams)
cmd.Flags().Set("filename", "../../../test/e2e/testing-manifests/guestbook/legacy")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -697,9 +698,9 @@ func TestDeleteMultipleSelector(t *testing.T) {
}),
}
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdDelete(tf, buf, errBuf)
cmd := NewCmdDelete(tf, streams)
cmd.Flags().Set("selector", "a=b")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -742,15 +743,15 @@ func TestResourceErrors(t *testing.T) {
tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig()
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
options := &DeleteOptions{
FilenameOptions: resource.FilenameOptions{},
GracePeriod: -1,
Cascade: false,
Output: "name",
IOStreams: streams,
}
err := options.Complete(tf, buf, errBuf, testCase.args, fakecmd())
err := options.Complete(tf, testCase.args, fakecmd())
if !testCase.errFn(err) {
t.Errorf("%s: unexpected error: %v", k, err)
return

View File

@ -34,6 +34,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/utils/exec"
@ -101,12 +102,11 @@ func parseDiffArguments(args []string) (string, string, error) {
return from, to, nil
}
func NewCmdDiff(f cmdutil.Factory, stdout, stderr io.Writer) *cobra.Command {
func NewCmdDiff(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
var options DiffOptions
diff := DiffProgram{
Exec: exec.New(),
Stdout: stdout,
Stderr: stderr,
Exec: exec.New(),
IOStreams: streams,
}
cmd := &cobra.Command{
Use: "diff -f FILENAME",
@ -132,9 +132,8 @@ func NewCmdDiff(f cmdutil.Factory, stdout, stderr io.Writer) *cobra.Command {
// KUBERNETES_EXTERNAL_DIFF environment variable will be used a diff
// program. By default, `diff(1)` will be used.
type DiffProgram struct {
Exec exec.Interface
Stdout io.Writer
Stderr io.Writer
Exec exec.Interface
genericclioptions.IOStreams
}
func (d *DiffProgram) getCommand(args ...string) exec.Cmd {
@ -147,8 +146,8 @@ func (d *DiffProgram) getCommand(args ...string) exec.Cmd {
}
cmd := d.Exec.Command(diff, args...)
cmd.SetStdout(d.Stdout)
cmd.SetStderr(d.Stderr)
cmd.SetStdout(d.Out)
cmd.SetStderr(d.ErrOut)
return cmd
}

View File

@ -25,6 +25,7 @@ import (
"strings"
"testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/utils/exec"
)
@ -137,11 +138,10 @@ func TestArguments(t *testing.T) {
func TestDiffProgram(t *testing.T) {
os.Setenv("KUBERNETES_EXTERNAL_DIFF", "echo")
stdout := bytes.Buffer{}
streams, _, stdout, _ := genericclioptions.NewTestIOStreams()
diff := DiffProgram{
Stdout: &stdout,
Stderr: &bytes.Buffer{},
Exec: exec.New(),
IOStreams: streams,
Exec: exec.New(),
}
err := diff.Run("one", "two")
if err != nil {

View File

@ -32,6 +32,7 @@ import (
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/kubectl/util/term"
"k8s.io/kubernetes/pkg/util/interrupt"
@ -62,12 +63,10 @@ const (
execUsageStr = "expected 'exec POD_NAME COMMAND [ARG1] [ARG2] ... [ARGN]'.\nPOD_NAME and COMMAND are required arguments for the exec command"
)
func NewCmdExec(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command {
func NewCmdExec(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
options := &ExecOptions{
StreamOptions: StreamOptions{
In: cmdIn,
Out: cmdOut,
Err: cmdErr,
IOStreams: streams,
},
Executor: &DefaultRemoteExecutor{},
@ -125,9 +124,8 @@ type StreamOptions struct {
Quiet bool
// InterruptParent, if set, is used to handle interrupts while attached
InterruptParent *interrupt.Handler
In io.Reader
Out io.Writer
Err io.Writer
genericclioptions.IOStreams
// for testing
overrideStreams func() (io.ReadCloser, io.Writer, io.Writer)
@ -155,7 +153,7 @@ func (p *ExecOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn []s
return cmdutil.UsageErrorf(cmd, execUsageStr)
}
if len(p.PodName) != 0 {
printDeprecationWarning(p.Err, "exec POD_NAME", "-p POD_NAME")
printDeprecationWarning(p.ErrOut, "exec POD_NAME", "-p POD_NAME")
if len(argsIn) < 1 {
return cmdutil.UsageErrorf(cmd, execUsageStr)
}
@ -205,7 +203,7 @@ func (p *ExecOptions) Validate() error {
if len(p.Command) == 0 {
return fmt.Errorf("you must specify at least one command for the container")
}
if p.Out == nil || p.Err == nil {
if p.Out == nil || p.ErrOut == nil {
return fmt.Errorf("both output and error output must be provided")
}
if p.Executor == nil || p.PodClient == nil || p.Config == nil {
@ -240,8 +238,8 @@ func (o *StreamOptions) setupTTY() term.TTY {
if !o.isTerminalIn(t) {
o.TTY = false
if o.Err != nil {
fmt.Fprintln(o.Err, "Unable to use a TTY - input is not a terminal or the right kind of file")
if o.ErrOut != nil {
fmt.Fprintln(o.ErrOut, "Unable to use a TTY - input is not a terminal or the right kind of file")
}
return t
@ -284,7 +282,7 @@ func (p *ExecOptions) Run() error {
if len(p.SuggestedCmdUsage) > 0 {
usageString = fmt.Sprintf("%s\n%s", usageString, p.SuggestedCmdUsage)
}
fmt.Fprintf(p.Err, "%s\n", usageString)
fmt.Fprintf(p.ErrOut, "%s\n", usageString)
}
containerName = pod.Spec.Containers[0].Name
}
@ -299,7 +297,7 @@ func (p *ExecOptions) Run() error {
// unset p.Err if it was previously set because both stdout and stderr go over p.Out when tty is
// true
p.Err = nil
p.ErrOut = nil
}
fn := func() error {
@ -320,11 +318,11 @@ func (p *ExecOptions) Run() error {
Command: p.Command,
Stdin: p.Stdin,
Stdout: p.Out != nil,
Stderr: p.Err != nil,
Stderr: p.ErrOut != nil,
TTY: t.Raw,
}, legacyscheme.ParameterCodec)
return p.Executor.Execute("POST", req.URL(), p.Config, p.In, p.Out, p.Err, t.Raw, sizeQueue)
return p.Executor.Execute("POST", req.URL(), p.Config, p.In, p.Out, p.ErrOut, t.Raw, sizeQueue)
}
if err := t.Safe(fn); err != nil {

View File

@ -36,6 +36,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/term"
)
@ -145,7 +146,7 @@ func TestPodAndContainer(t *testing.T) {
cmd := &cobra.Command{}
options := test.p
options.Err = bytes.NewBuffer([]byte{})
options.ErrOut = bytes.NewBuffer([]byte{})
err := options.Complete(tf, cmd, test.args, test.argsLenAtDash)
if test.expectError && err == nil {
t.Errorf("%s: unexpected non-error", test.name)
@ -213,9 +214,6 @@ func TestExec(t *testing.T) {
}
tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig()
bufOut := bytes.NewBuffer([]byte{})
bufErr := bytes.NewBuffer([]byte{})
bufIn := bytes.NewBuffer([]byte{})
ex := &fakeRemoteExecutor{}
if test.execErr {
ex.execErr = fmt.Errorf("exec error")
@ -224,9 +222,7 @@ func TestExec(t *testing.T) {
StreamOptions: StreamOptions{
PodName: "foo",
ContainerName: "bar",
In: bufIn,
Out: bufOut,
Err: bufErr,
IOStreams: genericclioptions.NewTestIOStreamsDiscard(),
},
Executor: ex,
}
@ -277,16 +273,14 @@ func execPod() *api.Pod {
}
func TestSetupTTY(t *testing.T) {
stderr := &bytes.Buffer{}
streams, _, _, stderr := genericclioptions.NewTestIOStreams()
// test 1 - don't attach stdin
o := &StreamOptions{
// InterruptParent: ,
Stdin: false,
In: &bytes.Buffer{},
Out: &bytes.Buffer{},
Err: stderr,
TTY: true,
Stdin: false,
IOStreams: streams,
TTY: true,
}
tty := o.setupTTY()
@ -334,7 +328,7 @@ func TestSetupTTY(t *testing.T) {
// test 3 - request a TTY, but stdin is not a terminal
o.Stdin = true
o.In = &bytes.Buffer{}
o.Err = stderr
o.ErrOut = stderr
o.TTY = true
tty = o.setupTTY()

View File

@ -35,6 +35,7 @@ import (
"k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/util"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
@ -89,12 +90,19 @@ type LogsOptions struct {
GetPodTimeout time.Duration
LogsForObject func(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error)
Out io.Writer
genericclioptions.IOStreams
}
func NewLogsOptions(streams genericclioptions.IOStreams) *LogsOptions {
return &LogsOptions{
IOStreams: streams,
}
}
// NewCmdLogs creates a new pod logs command
func NewCmdLogs(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
o := &LogsOptions{}
func NewCmdLogs(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
o := NewLogsOptions(streams)
cmd := &cobra.Command{
Use: "logs [-f] [-p] (POD | TYPE/NAME) [-c CONTAINER]",
DisableFlagsInUseLine: true,
@ -103,11 +111,11 @@ func NewCmdLogs(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
Example: logsExample,
PreRun: func(cmd *cobra.Command, args []string) {
if len(os.Args) > 1 && os.Args[1] == "log" {
printDeprecationWarning(errOut, "logs", "log")
printDeprecationWarning(o.ErrOut, "logs", "log")
}
},
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, out, cmd, args))
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunLogs())
},
@ -129,7 +137,7 @@ func NewCmdLogs(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
return cmd
}
func (o *LogsOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
func (o *LogsOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
containerName := cmdutil.GetFlagString(cmd, "container")
selector := cmdutil.GetFlagString(cmd, "selector")
o.AllContainers = cmdutil.GetFlagBool(cmd, "all-containers")
@ -189,7 +197,6 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Comm
}
o.Options = logOptions
o.LogsForObject = f.LogsForObject
o.Out = out
if len(selector) != 0 {
if logOptions.Follow {

View File

@ -20,7 +20,6 @@ import (
"bytes"
"io/ioutil"
"net/http"
"os"
"strings"
"testing"
@ -31,6 +30,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
@ -74,9 +74,9 @@ func TestLog(t *testing.T) {
}
tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdLogs(tf, buf, buf)
cmd := NewCmdLogs(tf, streams)
cmd.Flags().Set("namespace", "test")
cmd.Run(cmd, []string{"foo"})
@ -144,17 +144,17 @@ func TestValidateLogFlags(t *testing.T) {
},
}
for _, test := range tests {
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLogs(f, buf, buf)
streams := genericclioptions.NewTestIOStreamsDiscard()
cmd := NewCmdLogs(f, streams)
out := ""
for flag, value := range test.flags {
cmd.Flags().Set(flag, value)
}
// checkErr breaks tests in case of errors, plus we just
// need to check errors returned by the command validation
o := &LogsOptions{}
o := NewLogsOptions(streams)
cmd.Run = func(cmd *cobra.Command, args []string) {
o.Complete(f, os.Stdout, cmd, args)
o.Complete(f, cmd, args)
out = o.Validate().Error()
}
cmd.Run(cmd, test.args)
@ -205,8 +205,7 @@ func TestLogComplete(t *testing.T) {
},
}
for _, test := range tests {
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLogs(f, buf, buf)
cmd := NewCmdLogs(f, genericclioptions.NewTestIOStreamsDiscard())
var err error
out := ""
for flag, value := range test.flags {
@ -214,8 +213,8 @@ func TestLogComplete(t *testing.T) {
}
// checkErr breaks tests in case of errors, plus we just
// need to check errors returned by the command validation
o := &LogsOptions{}
err = o.Complete(f, os.Stdout, cmd, test.args)
o := NewLogsOptions(genericclioptions.NewTestIOStreamsDiscard())
err = o.Complete(f, cmd, test.args)
out = err.Error()
if !strings.Contains(out, test.expected) {
t.Errorf("%s: expected to find:\n\t%s\nfound:\n\t%s\n", test.name, test.expected, out)

View File

@ -18,7 +18,6 @@ package cmd
import (
"fmt"
"io"
"os"
"os/exec"
"syscall"
@ -28,6 +27,7 @@ import (
"github.com/spf13/pflag"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/plugins"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
@ -42,7 +42,7 @@ var (
)
// NewCmdPlugin creates the command that is the top-level for plugin commands.
func NewCmdPlugin(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cobra.Command {
func NewCmdPlugin(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
// Loads plugins and create commands for each plugin identified
loadedPlugins, loadErr := f.PluginLoader().Load()
if loadErr != nil {
@ -58,14 +58,14 @@ func NewCmdPlugin(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cobra.Co
if len(loadedPlugins) == 0 {
cmdutil.CheckErr(fmt.Errorf("no plugins installed."))
}
cmdutil.DefaultSubCommandRun(err)(cmd, args)
cmdutil.DefaultSubCommandRun(streams.ErrOut)(cmd, args)
},
}
if len(loadedPlugins) > 0 {
pluginRunner := f.PluginRunner()
for _, p := range loadedPlugins {
cmd.AddCommand(NewCmdForPlugin(f, p, pluginRunner, in, out, err))
cmd.AddCommand(NewCmdForPlugin(f, p, pluginRunner, streams))
}
}
@ -73,7 +73,7 @@ func NewCmdPlugin(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cobra.Co
}
// NewCmdForPlugin creates a command capable of running the provided plugin.
func NewCmdForPlugin(f cmdutil.Factory, plugin *plugins.Plugin, runner plugins.PluginRunner, in io.Reader, out, errout io.Writer) *cobra.Command {
func NewCmdForPlugin(f cmdutil.Factory, plugin *plugins.Plugin, runner plugins.PluginRunner, streams genericclioptions.IOStreams) *cobra.Command {
if !plugin.IsValid() {
return nil
}
@ -85,7 +85,7 @@ func NewCmdForPlugin(f cmdutil.Factory, plugin *plugins.Plugin, runner plugins.P
Example: templates.Examples(plugin.Example),
Run: func(cmd *cobra.Command, args []string) {
if len(plugin.Command) == 0 {
cmdutil.DefaultSubCommandRun(errout)(cmd, args)
cmdutil.DefaultSubCommandRun(streams.ErrOut)(cmd, args)
return
}
@ -104,9 +104,7 @@ func NewCmdForPlugin(f cmdutil.Factory, plugin *plugins.Plugin, runner plugins.P
}
runningContext := plugins.RunningContext{
In: in,
Out: out,
ErrOut: errout,
IOStreams: streams,
Args: args,
EnvProvider: envProvider,
WorkingDir: plugin.Dir,
@ -117,7 +115,7 @@ func NewCmdForPlugin(f cmdutil.Factory, plugin *plugins.Plugin, runner plugins.P
// check for (and exit with) the correct exit code
// from a failed plugin command execution
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
fmt.Fprintf(errout, "error: %v\n", err)
fmt.Fprintf(streams.ErrOut, "error: %v\n", err)
os.Exit(status.ExitStatus())
}
}
@ -132,7 +130,7 @@ func NewCmdForPlugin(f cmdutil.Factory, plugin *plugins.Plugin, runner plugins.P
}
for _, childPlugin := range plugin.Tree {
cmd.AddCommand(NewCmdForPlugin(f, childPlugin, runner, in, out, errout))
cmd.AddCommand(NewCmdForPlugin(f, childPlugin, runner, streams))
}
return cmd

View File

@ -17,12 +17,12 @@ limitations under the License.
package cmd
import (
"bytes"
"fmt"
"testing"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/plugins"
)
@ -81,9 +81,7 @@ func TestPluginCmd(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
inBuf := bytes.NewBuffer([]byte{})
outBuf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
streams, _, outBuf, errBuf := genericclioptions.NewTestIOStreams()
cmdutil.BehaviorOnFatal(func(str string, code int) {
errBuf.Write([]byte(str))
@ -96,7 +94,7 @@ func TestPluginCmd(t *testing.T) {
f := cmdtesting.NewTestFactory()
defer f.Cleanup()
cmd := NewCmdForPlugin(f, test.plugin, runner, inBuf, outBuf, errBuf)
cmd := NewCmdForPlugin(f, test.plugin, runner, streams)
if cmd == nil {
if !test.expectedNilCmd {
t.Fatalf("%s: command was unexpectedly not registered", test.name)

View File

@ -18,7 +18,6 @@ package cmd
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
@ -38,6 +37,7 @@ import (
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/util"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
@ -84,11 +84,10 @@ const (
defaultPodPortForwardWaitTimeout = 60 * time.Second
)
func NewCmdPortForward(f cmdutil.Factory, cmdOut, cmdErr io.Writer) *cobra.Command {
func NewCmdPortForward(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
opts := &PortForwardOptions{
PortForwarder: &defaultPortForwarder{
cmdOut: cmdOut,
cmdErr: cmdErr,
IOStreams: streams,
},
}
cmd := &cobra.Command{
@ -119,7 +118,7 @@ type portForwarder interface {
}
type defaultPortForwarder struct {
cmdOut, cmdErr io.Writer
genericclioptions.IOStreams
}
func (f *defaultPortForwarder) ForwardPorts(method string, url *url.URL, opts PortForwardOptions) error {
@ -128,7 +127,7 @@ func (f *defaultPortForwarder) ForwardPorts(method string, url *url.URL, opts Po
return err
}
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, method, url)
fw, err := portforward.New(dialer, opts.Ports, opts.StopChannel, opts.ReadyChannel, f.cmdOut, f.cmdErr)
fw, err := portforward.New(dialer, opts.Ports, opts.StopChannel, opts.ReadyChannel, f.Out, f.ErrOut)
if err != nil {
return err
}

View File

@ -20,7 +20,6 @@ import (
"fmt"
"net/http"
"net/url"
"os"
"reflect"
"testing"
@ -32,6 +31,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
@ -102,7 +102,7 @@ func testPortForward(t *testing.T, flags map[string]string, args []string) {
}
opts := &PortForwardOptions{}
cmd := NewCmdPortForward(tf, os.Stdout, os.Stderr)
cmd := NewCmdPortForward(tf, genericclioptions.NewTestIOStreamsDiscard())
cmd.Run = func(cmd *cobra.Command, args []string) {
if err = opts.Complete(tf, cmd, args); err != nil {
return

View File

@ -28,6 +28,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/proxy"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
@ -69,7 +70,7 @@ var (
kubectl proxy --api-prefix=/k8s-api`))
)
func NewCmdProxy(f cmdutil.Factory, out io.Writer) *cobra.Command {
func NewCmdProxy(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "proxy [--port=PORT] [--www=static-dir] [--www-prefix=prefix] [--api-prefix=prefix]",
DisableFlagsInUseLine: true,
@ -77,7 +78,7 @@ func NewCmdProxy(f cmdutil.Factory, out io.Writer) *cobra.Command {
Long: proxyLong,
Example: proxyExample,
Run: func(cmd *cobra.Command, args []string) {
err := RunProxy(f, out, cmd)
err := RunProxy(f, streams.Out, cmd)
cmdutil.CheckErr(err)
},
}

View File

@ -18,7 +18,6 @@ package cmd
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
@ -86,11 +85,10 @@ type ReplaceOptions struct {
Recorder genericclioptions.Recorder
Out io.Writer
ErrOut io.Writer
genericclioptions.IOStreams
}
func NewReplaceOptions(out, errOut io.Writer) *ReplaceOptions {
func NewReplaceOptions(streams genericclioptions.IOStreams) *ReplaceOptions {
outputFormat := ""
return &ReplaceOptions{
@ -102,13 +100,12 @@ func NewReplaceOptions(out, errOut io.Writer) *ReplaceOptions {
},
DeleteFlags: NewDeleteFlags("to use to replace the resource."),
Out: out,
ErrOut: errOut,
IOStreams: streams,
}
}
func NewCmdReplace(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
o := NewReplaceOptions(out, errOut)
func NewCmdReplace(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
o := NewReplaceOptions(streams)
cmd := &cobra.Command{
Use: "replace -f FILENAME",
@ -154,7 +151,7 @@ func (o *ReplaceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
return printer.PrintObj(obj, o.Out)
}
deleteOpts := o.DeleteFlags.ToOptions(o.Out, o.ErrOut)
deleteOpts := o.DeleteFlags.ToOptions(o.IOStreams)
//Replace will create a resource if it doesn't exist already, so ignore not found error
deleteOpts.IgnoreNotFound = true

View File

@ -17,7 +17,6 @@ limitations under the License.
package cmd
import (
"bytes"
"net/http"
"strings"
"testing"
@ -26,6 +25,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
@ -63,9 +63,9 @@ func TestReplaceObject(t *testing.T) {
}),
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdReplace(tf, buf, buf)
cmd := NewCmdReplace(tf, streams)
cmd.Flags().Set("filename", "../../../test/e2e/testing-manifests/guestbook/legacy/redis-master-controller.yaml")
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{})
@ -134,9 +134,9 @@ func TestReplaceMultipleObject(t *testing.T) {
}),
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdReplace(tf, buf, buf)
cmd := NewCmdReplace(tf, streams)
cmd.Flags().Set("filename", "../../../test/e2e/testing-manifests/guestbook/legacy/redis-master-controller.yaml")
cmd.Flags().Set("filename", "../../../test/e2e/testing-manifests/guestbook/frontend-service.yaml")
cmd.Flags().Set("output", "name")
@ -192,9 +192,9 @@ func TestReplaceDirectory(t *testing.T) {
}),
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdReplace(tf, buf, buf)
cmd := NewCmdReplace(tf, streams)
cmd.Flags().Set("filename", "../../../test/e2e/testing-manifests/guestbook/legacy")
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("output", "name")
@ -239,9 +239,9 @@ func TestForceReplaceObjectNotFound(t *testing.T) {
}),
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdReplace(tf, buf, buf)
cmd := NewCmdReplace(tf, streams)
cmd.Flags().Set("filename", "../../../test/e2e/testing-manifests/guestbook/legacy/redis-master-controller.yaml")
cmd.Flags().Set("force", "true")
cmd.Flags().Set("cascade", "false")

View File

@ -233,7 +233,7 @@ func (o *RunOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
return printer.PrintObj(obj, o.Out)
}
deleteOpts := o.DeleteFlags.ToOptions(o.Out, o.ErrOut)
deleteOpts := o.DeleteFlags.ToOptions(o.IOStreams)
deleteOpts.IgnoreNotFound = true
deleteOpts.WaitForDeletion = false
deleteOpts.GracePeriod = -1
@ -374,12 +374,10 @@ func (o *RunOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
opts := &AttachOptions{
StreamOptions: StreamOptions{
In: o.In,
Out: o.Out,
Err: o.ErrOut,
Stdin: o.Interactive,
TTY: o.TTY,
Quiet: o.Quiet,
IOStreams: o.IOStreams,
Stdin: o.Interactive,
TTY: o.TTY,
Quiet: o.Quiet,
},
GetPodTimeout: timeout,
CommandName: cmd.Parent().CommandPath() + " attach",
@ -528,7 +526,7 @@ func handleAttachPod(f cmdutil.Factory, podClient coreclient.PodsGetter, ns, nam
opts.Namespace = ns
// TODO: opts.Run sets opts.Err to nil, we need to find a better way
stderr := opts.Err
stderr := opts.ErrOut
if err := opts.Run(); err != nil {
fmt.Fprintf(stderr, "Error attaching, falling back to logs: %v\n", err)
return logOpts(f, pod, opts)

View File

@ -208,7 +208,7 @@ func TestRunArgsFollowDashRules(t *testing.T) {
deleteFlags := NewDeleteFlags("to use to replace the resource.")
opts := &RunOptions{
PrintFlags: printFlags,
DeleteOptions: deleteFlags.ToOptions(os.Stdout, os.Stderr),
DeleteOptions: deleteFlags.ToOptions(genericclioptions.NewTestIOStreamsDiscard()),
IOStreams: genericclioptions.NewTestIOStreamsDiscard(),
@ -377,7 +377,7 @@ func TestGenerateService(t *testing.T) {
deleteFlags := NewDeleteFlags("to use to replace the resource.")
opts := &RunOptions{
PrintFlags: printFlags,
DeleteOptions: deleteFlags.ToOptions(os.Stdout, os.Stderr),
DeleteOptions: deleteFlags.ToOptions(genericclioptions.NewTestIOStreamsDiscard()),
IOStreams: ioStreams,

View File

@ -17,8 +17,6 @@ limitations under the License.
package cmd
import (
"io"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
@ -26,6 +24,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
var (
@ -40,17 +39,17 @@ var (
This command requires Heapster to be correctly configured and working on the server. `))
)
func NewCmdTop(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
func NewCmdTop(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "top",
Short: i18n.T("Display Resource (CPU/Memory/Storage) usage."),
Long: topLong,
Run: cmdutil.DefaultSubCommandRun(errOut),
Run: cmdutil.DefaultSubCommandRun(streams.ErrOut),
}
// create subcommands
cmd.AddCommand(NewCmdTopNode(f, nil, out))
cmd.AddCommand(NewCmdTopPod(f, nil, out))
cmd.AddCommand(NewCmdTopNode(f, nil, streams))
cmd.AddCommand(NewCmdTopPod(f, nil, streams))
return cmd
}

View File

@ -18,7 +18,6 @@ package cmd
import (
"errors"
"io"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
@ -29,6 +28,7 @@ import (
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/metricsutil"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
metricsapi "k8s.io/metrics/pkg/apis/metrics"
@ -46,6 +46,8 @@ type TopNodeOptions struct {
Printer *metricsutil.TopCmdPrinter
DiscoveryClient discovery.DiscoveryInterface
MetricsClient metricsclientset.Interface
genericclioptions.IOStreams
}
type HeapsterTopOptions struct {
@ -89,9 +91,11 @@ var (
kubectl top node NODE_NAME`))
)
func NewCmdTopNode(f cmdutil.Factory, options *TopNodeOptions, out io.Writer) *cobra.Command {
if options == nil {
options = &TopNodeOptions{}
func NewCmdTopNode(f cmdutil.Factory, o *TopNodeOptions, streams genericclioptions.IOStreams) *cobra.Command {
if o == nil {
o = &TopNodeOptions{
IOStreams: streams,
}
}
cmd := &cobra.Command{
@ -101,24 +105,24 @@ func NewCmdTopNode(f cmdutil.Factory, options *TopNodeOptions, out io.Writer) *c
Long: topNodeLong,
Example: topNodeExample,
Run: func(cmd *cobra.Command, args []string) {
if err := options.Complete(f, cmd, args, out); err != nil {
if err := o.Complete(f, cmd, args); err != nil {
cmdutil.CheckErr(err)
}
if err := options.Validate(); err != nil {
if err := o.Validate(); err != nil {
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, "%v", err))
}
if err := options.RunTopNode(); err != nil {
if err := o.RunTopNode(); err != nil {
cmdutil.CheckErr(err)
}
},
Aliases: []string{"nodes", "no"},
}
cmd.Flags().StringVarP(&options.Selector, "selector", "l", options.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
options.HeapsterOptions.Bind(cmd.Flags())
cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
o.HeapsterOptions.Bind(cmd.Flags())
return cmd
}
func (o *TopNodeOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string, out io.Writer) error {
func (o *TopNodeOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
if len(args) == 1 {
o.ResourceName = args[0]
} else if len(args) > 1 {
@ -144,7 +148,7 @@ func (o *TopNodeOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
o.NodeClient = clientset.CoreV1()
o.Client = metricsutil.NewHeapsterMetricsClient(clientset.CoreV1(), o.HeapsterOptions.Namespace, o.HeapsterOptions.Scheme, o.HeapsterOptions.Service, o.HeapsterOptions.Port)
o.Printer = metricsutil.NewTopCmdPrinter(out)
o.Printer = metricsutil.NewTopCmdPrinter(o.Out)
return nil
}

View File

@ -32,6 +32,7 @@ import (
core "k8s.io/client-go/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/scheme"
metricsv1alpha1api "k8s.io/metrics/pkg/apis/metrics/v1alpha1"
metricsv1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
@ -79,9 +80,9 @@ func TestTopNodeAllMetrics(t *testing.T) {
}
tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdTopNode(tf, nil, buf)
cmd := NewCmdTopNode(tf, nil, streams)
cmd.Run(cmd, []string{})
// Check the presence of node names in the output.
@ -132,7 +133,7 @@ func TestTopNodeAllMetricsCustomDefaults(t *testing.T) {
}
tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
opts := &TopNodeOptions{
HeapsterOptions: HeapsterTopOptions{
@ -140,8 +141,9 @@ func TestTopNodeAllMetricsCustomDefaults(t *testing.T) {
Scheme: "https",
Service: "custom-heapster-service",
},
IOStreams: streams,
}
cmd := NewCmdTopNode(tf, opts, buf)
cmd := NewCmdTopNode(tf, opts, streams)
cmd.Run(cmd, []string{})
// Check the presence of node names in the output.
@ -195,9 +197,9 @@ func TestTopNodeWithNameMetrics(t *testing.T) {
}
tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdTopNode(tf, nil, buf)
cmd := NewCmdTopNode(tf, nil, streams)
cmd.Run(cmd, []string{expectedMetrics.Name})
// Check the presence of node names in the output.
@ -262,9 +264,9 @@ func TestTopNodeWithLabelSelectorMetrics(t *testing.T) {
}
tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdTopNode(tf, nil, buf)
cmd := NewCmdTopNode(tf, nil, streams)
cmd.Flags().Set("selector", label)
cmd.Run(cmd, []string{})
@ -315,14 +317,16 @@ func TestTopNodeAllMetricsFromMetricsServer(t *testing.T) {
})
tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdTopNode(tf, nil, buf)
cmd := NewCmdTopNode(tf, nil, streams)
// TODO in the long run, we want to test most of our commands like this. Wire the options struct with specific mocks
// TODO then check the particular Run functionality and harvest results from fake clients
cmdOptions := &TopNodeOptions{}
if err := cmdOptions.Complete(tf, cmd, []string{}, buf); err != nil {
cmdOptions := &TopNodeOptions{
IOStreams: streams,
}
if err := cmdOptions.Complete(tf, cmd, []string{}); err != nil {
t.Fatal(err)
}
cmdOptions.MetricsClient = fakemetricsClientset
@ -381,14 +385,16 @@ func TestTopNodeWithNameMetricsFromMetricsServer(t *testing.T) {
})
tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdTopNode(tf, nil, buf)
cmd := NewCmdTopNode(tf, nil, streams)
// TODO in the long run, we want to test most of our commands like this. Wire the options struct with specific mocks
// TODO then check the particular Run functionality and harvest results from fake clients
cmdOptions := &TopNodeOptions{}
if err := cmdOptions.Complete(tf, cmd, []string{expectedMetrics.Name}, buf); err != nil {
cmdOptions := &TopNodeOptions{
IOStreams: streams,
}
if err := cmdOptions.Complete(tf, cmd, []string{expectedMetrics.Name}); err != nil {
t.Fatal(err)
}
cmdOptions.MetricsClient = fakemetricsClientset
@ -458,15 +464,17 @@ func TestTopNodeWithLabelSelectorMetricsFromMetricsServer(t *testing.T) {
})
tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdTopNode(tf, nil, buf)
cmd := NewCmdTopNode(tf, nil, streams)
cmd.Flags().Set("selector", label)
// TODO in the long run, we want to test most of our commands like this. Wire the options struct with specific mocks
// TODO then check the particular Run functionality and harvest results from fake clients
cmdOptions := &TopNodeOptions{}
if err := cmdOptions.Complete(tf, cmd, []string{}, buf); err != nil {
cmdOptions := &TopNodeOptions{
IOStreams: streams,
}
if err := cmdOptions.Complete(tf, cmd, []string{}); err != nil {
t.Fatal(err)
}
cmdOptions.MetricsClient = fakemetricsClientset

View File

@ -19,7 +19,6 @@ package cmd
import (
"errors"
"fmt"
"io"
"time"
"k8s.io/api/core/v1"
@ -37,6 +36,7 @@ import (
"github.com/golang/glog"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
type TopPodOptions struct {
@ -51,6 +51,8 @@ type TopPodOptions struct {
Printer *metricsutil.TopCmdPrinter
DiscoveryClient discovery.DiscoveryInterface
MetricsClient metricsclientset.Interface
genericclioptions.IOStreams
}
const metricsCreationDelay = 2 * time.Minute
@ -78,9 +80,11 @@ var (
kubectl top pod -l name=myLabel`))
)
func NewCmdTopPod(f cmdutil.Factory, options *TopPodOptions, out io.Writer) *cobra.Command {
if options == nil {
options = &TopPodOptions{}
func NewCmdTopPod(f cmdutil.Factory, o *TopPodOptions, streams genericclioptions.IOStreams) *cobra.Command {
if o == nil {
o = &TopPodOptions{
IOStreams: streams,
}
}
cmd := &cobra.Command{
@ -90,26 +94,26 @@ func NewCmdTopPod(f cmdutil.Factory, options *TopPodOptions, out io.Writer) *cob
Long: topPodLong,
Example: topPodExample,
Run: func(cmd *cobra.Command, args []string) {
if err := options.Complete(f, cmd, args, out); err != nil {
if err := o.Complete(f, cmd, args); err != nil {
cmdutil.CheckErr(err)
}
if err := options.Validate(); err != nil {
if err := o.Validate(); err != nil {
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, "%v", err))
}
if err := options.RunTopPod(); err != nil {
if err := o.RunTopPod(); err != nil {
cmdutil.CheckErr(err)
}
},
Aliases: []string{"pods", "po"},
}
cmd.Flags().StringVarP(&options.Selector, "selector", "l", options.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmd.Flags().BoolVar(&options.PrintContainers, "containers", options.PrintContainers, "If present, print usage of containers within a pod.")
cmd.Flags().BoolVar(&options.AllNamespaces, "all-namespaces", options.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
options.HeapsterOptions.Bind(cmd.Flags())
cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmd.Flags().BoolVar(&o.PrintContainers, "containers", o.PrintContainers, "If present, print usage of containers within a pod.")
cmd.Flags().BoolVar(&o.AllNamespaces, "all-namespaces", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
o.HeapsterOptions.Bind(cmd.Flags())
return cmd
}
func (o *TopPodOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string, out io.Writer) error {
func (o *TopPodOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
if len(args) == 1 {
o.ResourceName = args[0]
@ -139,7 +143,7 @@ func (o *TopPodOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []s
o.PodClient = clientset.CoreV1()
o.Client = metricsutil.NewHeapsterMetricsClient(clientset.CoreV1(), o.HeapsterOptions.Namespace, o.HeapsterOptions.Scheme, o.HeapsterOptions.Service, o.HeapsterOptions.Port)
o.Printer = metricsutil.NewTopCmdPrinter(out)
o.Printer = metricsutil.NewTopCmdPrinter(o.Out)
return nil
}

View File

@ -37,6 +37,7 @@ import (
core "k8s.io/client-go/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
metricsv1alpha1api "k8s.io/metrics/pkg/apis/metrics/v1alpha1"
metricsv1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
metricsfake "k8s.io/metrics/pkg/client/clientset_generated/clientset/fake"
@ -191,9 +192,9 @@ func TestTopPod(t *testing.T) {
}
tf.Namespace = testNS
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdTopPod(tf, nil, buf)
cmd := NewCmdTopPod(tf, nil, streams)
for name, value := range testCase.flags {
cmd.Flags().Set(name, value)
}
@ -330,19 +331,20 @@ func TestTopPodWithMetricsServer(t *testing.T) {
}
tf.Namespace = testNS
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdTopPod(tf, nil, buf)
cmd := NewCmdTopPod(tf, nil, streams)
var cmdOptions *TopPodOptions
if testCase.options != nil {
cmdOptions = testCase.options
} else {
cmdOptions = &TopPodOptions{}
}
cmdOptions.IOStreams = streams
// TODO in the long run, we want to test most of our commands like this. Wire the options struct with specific mocks
// TODO then check the particular Run functionality and harvest results from fake clients. We probably end up skipping the factory altogether.
if err := cmdOptions.Complete(tf, cmd, testCase.args, buf); err != nil {
if err := cmdOptions.Complete(tf, cmd, testCase.args); err != nil {
t.Fatal(err)
}
cmdOptions.MetricsClient = fakemetricsClientset
@ -534,7 +536,7 @@ func TestTopPodCustomDefaults(t *testing.T) {
}
tf.Namespace = testNS
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
opts := &TopPodOptions{
HeapsterOptions: HeapsterTopOptions{
@ -543,8 +545,9 @@ func TestTopPodCustomDefaults(t *testing.T) {
Service: "custom-heapster-service",
},
DiscoveryClient: &fakeDiscovery{},
IOStreams: streams,
}
cmd := NewCmdTopPod(tf, opts, buf)
cmd := NewCmdTopPod(tf, opts, streams)
for name, value := range testCase.flags {
cmd.Flags().Set(name, value)
}

View File

@ -29,6 +29,7 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
metricsv1alpha1api "k8s.io/metrics/pkg/apis/metrics/v1alpha1"
metricsv1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
)
@ -46,9 +47,7 @@ func TestTopSubcommandsExist(t *testing.T) {
f := cmdtesting.NewTestFactory()
defer f.Cleanup()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdTop(f, buf, buf)
cmd := NewCmdTop(f, genericclioptions.NewTestIOStreamsDiscard())
if !cmd.HasSubCommands() {
t.Error("top command should have subcommands")
}

View File

@ -16,6 +16,7 @@ go_library(
],
importpath = "k8s.io/kubernetes/pkg/kubectl/plugins",
deps = [
"//pkg/kubectl/genericclioptions:go_default_library",
"//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library",
@ -45,5 +46,8 @@ go_test(
"runner_test.go",
],
embed = [":go_default_library"],
deps = ["//vendor/github.com/spf13/pflag:go_default_library"],
deps = [
"//pkg/kubectl/genericclioptions:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library",
],
)

View File

@ -17,12 +17,12 @@ limitations under the License.
package plugins
import (
"io"
"os"
"os/exec"
"strings"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
// PluginRunner is capable of running a plugin in a given running context.
@ -34,9 +34,7 @@ type PluginRunner interface {
// in, out, and err streams, arguments and environment passed to it, and the
// working directory.
type RunningContext struct {
In io.Reader
Out io.Writer
ErrOut io.Writer
genericclioptions.IOStreams
Args []string
EnvProvider EnvProvider
WorkingDir string

View File

@ -17,9 +17,10 @@ limitations under the License.
package plugins
import (
"bytes"
"os"
"testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
func TestExecRunner(t *testing.T) {
@ -50,7 +51,7 @@ func TestExecRunner(t *testing.T) {
defer os.Unsetenv("KUBECTL_PLUGINS_TEST")
for _, test := range tests {
outBuf := bytes.NewBuffer([]byte{})
streams, _, outBuf, _ := genericclioptions.NewTestIOStreams()
plugin := &Plugin{
Description: Description{
@ -61,7 +62,7 @@ func TestExecRunner(t *testing.T) {
}
ctx := RunningContext{
Out: outBuf,
IOStreams: streams,
WorkingDir: ".",
EnvProvider: &EmptyEnvProvider{},
}