From eefaafc2140ea508d4ae32959124b1539427cb33 Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Tue, 24 Feb 2015 06:50:22 -0800 Subject: [PATCH] Prompt the user to select a container if they don't provide one and we can't infer it. --- docs/kubectl-log.md | 1 + docs/man/man1/kubectl-log.1 | 4 + pkg/kubectl/cmd/log.go | 39 +++++++++- pkg/kubectl/cmd/log_test.go | 147 ++++++++++++++++++++++++++++++++++++ 4 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 pkg/kubectl/cmd/log_test.go diff --git a/docs/kubectl-log.md b/docs/kubectl-log.md index 5cc009e87ac9b..ca1a42663fafb 100644 --- a/docs/kubectl-log.md +++ b/docs/kubectl-log.md @@ -24,6 +24,7 @@ $ kubectl log -f 123456-7890 ruby-container ``` -f, --follow=false: Specify if the logs should be streamed. + --interactive=true: If true, prompt the user for input when required. Default true. ``` ### Options inherrited from parent commands diff --git a/docs/man/man1/kubectl-log.1 b/docs/man/man1/kubectl-log.1 index ed12a6289e191..bf0bd6853e31b 100644 --- a/docs/man/man1/kubectl-log.1 +++ b/docs/man/man1/kubectl-log.1 @@ -21,6 +21,10 @@ Print the logs for a container in a pod. If the pod has only one container, the \fB\-f\fP, \fB\-\-follow\fP=false Specify if the logs should be streamed. +.PP +\fB\-\-interactive\fP=true + If true, prompt the user for input when required. Default true. + .SH OPTIONS INHERITED FROM PARENT COMMANDS .PP diff --git a/pkg/kubectl/cmd/log.go b/pkg/kubectl/cmd/log.go index ddf546c6299de..c8484b42a9139 100644 --- a/pkg/kubectl/cmd/log.go +++ b/pkg/kubectl/cmd/log.go @@ -17,10 +17,14 @@ limitations under the License. package cmd import ( + "fmt" "io" + "os" "strconv" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" + libutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/spf13/cobra" ) @@ -32,6 +36,28 @@ $ kubectl log 123456-7890 ruby-container $ kubectl log -f 123456-7890 ruby-container` ) +func selectContainer(pod *api.Pod, in io.Reader, out io.Writer) string { + fmt.Fprintf(out, "Please select a container:\n") + options := libutil.StringSet{} + for ix := range pod.Spec.Containers { + fmt.Fprintf(out, "[%d] %s\n", ix+1, pod.Spec.Containers[ix].Name) + options.Insert(pod.Spec.Containers[ix].Name) + } + for { + var input string + fmt.Fprintf(out, "> ") + fmt.Fscanln(in, &input) + if options.Has(input) { + return input + } + ix, err := strconv.Atoi(input) + if err == nil && ix > 0 && ix <= len(pod.Spec.Containers) { + return pod.Spec.Containers[ix-1].Name + } + fmt.Fprintf(out, "Invalid input: %s", input) + } +} + func (f *Factory) NewCmdLog(out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "log [-f] []", @@ -60,11 +86,15 @@ func (f *Factory) NewCmdLog(out io.Writer) *cobra.Command { var container string if len(args) == 1 { if len(pod.Spec.Containers) != 1 { - usageError(cmd, " is required for pods with multiple containers") + if !util.GetFlagBool(cmd, "interactive") { + usageError(cmd, " is required for pods with multiple containers") + } else { + container = selectContainer(pod, os.Stdin, out) + } + } else { + // Get logs for the only container in the pod + container = pod.Spec.Containers[0].Name } - - // Get logs for the only container in the pod - container = pod.Spec.Containers[0].Name } else { container = args[1] } @@ -89,5 +119,6 @@ func (f *Factory) NewCmdLog(out io.Writer) *cobra.Command { }, } cmd.Flags().BoolP("follow", "f", false, "Specify if the logs should be streamed.") + cmd.Flags().Bool("interactive", true, "If true, prompt the user for input when required. Default true.") return cmd } diff --git a/pkg/kubectl/cmd/log_test.go b/pkg/kubectl/cmd/log_test.go new file mode 100644 index 0000000000000..fcd93191a8281 --- /dev/null +++ b/pkg/kubectl/cmd/log_test.go @@ -0,0 +1,147 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "bytes" + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" +) + +func TestSelectContainer(t *testing.T) { + tests := []struct { + input string + pod api.Pod + expectedContainer string + }{ + { + input: "1\n", + pod: api.Pod{ + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "foo", + }, + }, + }, + }, + expectedContainer: "foo", + }, + { + input: "foo\n", + pod: api.Pod{ + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "foo", + }, + }, + }, + }, + expectedContainer: "foo", + }, + { + input: "foo\n", + pod: api.Pod{ + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "bar", + }, + { + Name: "foo", + }, + }, + }, + }, + expectedContainer: "foo", + }, + { + input: "2\n", + pod: api.Pod{ + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "bar", + }, + { + Name: "foo", + }, + }, + }, + }, + expectedContainer: "foo", + }, + { + input: "-1\n2\n", + pod: api.Pod{ + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "bar", + }, + { + Name: "foo", + }, + }, + }, + }, + expectedContainer: "foo", + }, + { + input: "3\n2\n", + pod: api.Pod{ + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "bar", + }, + { + Name: "foo", + }, + }, + }, + }, + expectedContainer: "foo", + }, + { + input: "baz\n2\n", + pod: api.Pod{ + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "bar", + }, + { + Name: "foo", + }, + }, + }, + }, + expectedContainer: "foo", + }, + } + + for _, test := range tests { + var buff bytes.Buffer + container := selectContainer(&test.pod, bytes.NewBufferString(test.input), &buff) + if container != test.expectedContainer { + t.Errorf("unexpected output: %s for input: %s", container, test.input) + } + } +}