From 3a3633a32ec83675b63c126932d00390165d3d54 Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Mon, 30 Jul 2018 15:26:41 -0400 Subject: [PATCH] update logs --- pkg/kubectl/cmd/BUILD | 2 - pkg/kubectl/cmd/logs.go | 170 ++++++++++++++-------- pkg/kubectl/cmd/logs_test.go | 274 +++++++++++++++++++++-------------- 3 files changed, 274 insertions(+), 172 deletions(-) diff --git a/pkg/kubectl/cmd/BUILD b/pkg/kubectl/cmd/BUILD index 2dc32bb870c..eb7721e31c4 100644 --- a/pkg/kubectl/cmd/BUILD +++ b/pkg/kubectl/cmd/BUILD @@ -60,7 +60,6 @@ go_library( "//pkg/apis/certificates:go_default_library", "//pkg/apis/core:go_default_library", "//pkg/apis/core/v1:go_default_library", - "//pkg/apis/core/validation:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library", @@ -197,7 +196,6 @@ go_test( "//pkg/apis/batch:go_default_library", "//pkg/apis/core:go_default_library", "//pkg/apis/extensions:go_default_library", - "//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/kubectl/cmd/create:go_default_library", "//pkg/kubectl/cmd/testing:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", diff --git a/pkg/kubectl/cmd/logs.go b/pkg/kubectl/cmd/logs.go index 79acc9af55e..69821f2a217 100644 --- a/pkg/kubectl/cmd/logs.go +++ b/pkg/kubectl/cmd/logs.go @@ -25,11 +25,10 @@ import ( "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" - api "k8s.io/kubernetes/pkg/apis/core" - "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" @@ -80,6 +79,24 @@ type LogsOptions struct { ResourceArg string AllContainers bool Options runtime.Object + Resources []string + + ConsumeRequestFn func(*rest.Request, io.Writer) error + + // PodLogOptions + SinceTime string + SinceSeconds time.Duration + Follow bool + Previous bool + Timestamps bool + LimitBytes int64 + Tail int64 + Container string + + // whether or not a container name was given via --container + ContainerNameSpecified bool + Interactive bool + Selector string Object runtime.Object GetPodTimeout time.Duration @@ -93,6 +110,7 @@ func NewLogsOptions(streams genericclioptions.IOStreams, allContainers bool) *Lo return &LogsOptions{ IOStreams: streams, AllContainers: allContainers, + Tail: -1, } } @@ -119,40 +137,74 @@ func NewCmdLogs(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C Aliases: []string{"log"}, } cmd.Flags().BoolVar(&o.AllContainers, "all-containers", o.AllContainers, "Get all containers's logs in the pod(s).") - cmd.Flags().BoolP("follow", "f", false, "Specify if the logs should be streamed.") - cmd.Flags().Bool("timestamps", false, "Include timestamps on each line in the log output") - cmd.Flags().Int64("limit-bytes", 0, "Maximum bytes of logs to return. Defaults to no limit.") - cmd.Flags().BoolP("previous", "p", false, "If true, print the logs for the previous instance of the container in a pod if it exists.") - cmd.Flags().Int64("tail", -1, "Lines of recent log file to display. Defaults to -1 with no selector, showing all log lines otherwise 10, if a selector is provided.") - cmd.Flags().String("since-time", "", i18n.T("Only return logs after a specific date (RFC3339). Defaults to all logs. Only one of since-time / since may be used.")) - cmd.Flags().Duration("since", 0, "Only return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to all logs. Only one of since-time / since may be used.") - cmd.Flags().StringP("container", "c", "", "Print the logs of this container") - cmd.Flags().Bool("interactive", false, "If true, prompt the user for input when required.") + cmd.Flags().BoolVarP(&o.Follow, "follow", "f", o.Follow, "Specify if the logs should be streamed.") + cmd.Flags().BoolVar(&o.Timestamps, "timestamps", o.Timestamps, "Include timestamps on each line in the log output") + cmd.Flags().Int64Var(&o.LimitBytes, "limit-bytes", o.LimitBytes, "Maximum bytes of logs to return. Defaults to no limit.") + cmd.Flags().BoolVarP(&o.Previous, "previous", "p", o.Previous, "If true, print the logs for the previous instance of the container in a pod if it exists.") + cmd.Flags().Int64Var(&o.Tail, "tail", o.Tail, "Lines of recent log file to display. Defaults to -1 with no selector, showing all log lines otherwise 10, if a selector is provided.") + cmd.Flags().StringVar(&o.SinceTime, "since-time", o.SinceTime, i18n.T("Only return logs after a specific date (RFC3339). Defaults to all logs. Only one of since-time / since may be used.")) + cmd.Flags().DurationVar(&o.SinceSeconds, "since", o.SinceSeconds, "Only return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to all logs. Only one of since-time / since may be used.") + cmd.Flags().StringVarP(&o.Container, "container", "c", o.Container, "Print the logs of this container") + cmd.Flags().BoolVar(&o.Interactive, "interactive", o.Interactive, "If true, prompt the user for input when required.") cmd.Flags().MarkDeprecated("interactive", "This flag is no longer respected and there is no replacement.") cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodLogsTimeout) - cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on.") + cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on.") return cmd } +func (o *LogsOptions) ToLogOptions() (*corev1.PodLogOptions, error) { + logOptions := &corev1.PodLogOptions{ + Container: o.Container, + Follow: o.Follow, + Previous: o.Previous, + Timestamps: o.Timestamps, + } + + if len(o.SinceTime) > 0 { + t, err := util.ParseRFC3339(o.SinceTime, metav1.Now) + if err != nil { + return nil, err + } + + logOptions.SinceTime = &t + } + + if o.LimitBytes != 0 { + logOptions.LimitBytes = &o.LimitBytes + } + + if o.SinceSeconds != 0 { + // round up to the nearest second + sec := int64(o.SinceSeconds.Round(time.Second).Seconds()) + logOptions.SinceSeconds = &sec + } + + if len(o.Selector) > 0 && o.Tail != -1 { + logOptions.TailLines = &selectorTail + } else if o.Tail != -1 { + logOptions.TailLines = &o.Tail + } + + return logOptions, nil +} + func (o *LogsOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { - containerName := cmdutil.GetFlagString(cmd, "container") - selector := cmdutil.GetFlagString(cmd, "selector") + o.ContainerNameSpecified = cmd.Flag("container").Changed + o.Resources = args + switch len(args) { case 0: - if len(selector) == 0 { + if len(o.Selector) == 0 { return cmdutil.UsageErrorf(cmd, "%s", logsUsageStr) } case 1: o.ResourceArg = args[0] - if len(selector) != 0 { + if len(o.Selector) != 0 { return cmdutil.UsageErrorf(cmd, "only a selector (-l) or a POD name is allowed") } case 2: - if cmd.Flag("container").Changed { - return cmdutil.UsageErrorf(cmd, "only one of -c or an inline [CONTAINER] arg is allowed") - } o.ResourceArg = args[0] - containerName = args[1] + o.Container = args[1] default: return cmdutil.UsageErrorf(cmd, "%s", logsUsageStr) } @@ -162,48 +214,21 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []str return err } - logOptions := &api.PodLogOptions{ - Container: containerName, - Follow: cmdutil.GetFlagBool(cmd, "follow"), - Previous: cmdutil.GetFlagBool(cmd, "previous"), - Timestamps: cmdutil.GetFlagBool(cmd, "timestamps"), - } - if sinceTime := cmdutil.GetFlagString(cmd, "since-time"); len(sinceTime) > 0 { - t, err := util.ParseRFC3339(sinceTime, metav1.Now) - if err != nil { - return err - } - logOptions.SinceTime = &t - } - if limit := cmdutil.GetFlagInt64(cmd, "limit-bytes"); limit != 0 { - logOptions.LimitBytes = &limit - } - tail := cmdutil.GetFlagInt64(cmd, "tail") - if tail != -1 { - logOptions.TailLines = &tail - } - if sinceSeconds := cmdutil.GetFlagDuration(cmd, "since"); sinceSeconds != 0 { - // round up to the nearest second - sec := int64(sinceSeconds.Round(time.Second).Seconds()) - logOptions.SinceSeconds = &sec - } + o.ConsumeRequestFn = consumeRequest + o.GetPodTimeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd) if err != nil { return err } - o.Options = logOptions + + o.Options, err = o.ToLogOptions() + if err != nil { + return err + } + o.RESTClientGetter = f o.LogsForObject = polymorphichelpers.LogsForObjectFn - if len(selector) != 0 { - if logOptions.Follow { - return cmdutil.UsageErrorf(cmd, "only one of follow (-f) or selector (-l) is allowed") - } - if logOptions.TailLines == nil && tail != -1 { - logOptions.TailLines = &selectorTail - } - } - if o.Object == nil { builder := f.NewBuilder(). WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). @@ -212,14 +237,14 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []str if o.ResourceArg != "" { builder.ResourceNames("pods", o.ResourceArg) } - if selector != "" { - builder.ResourceTypes("pods").LabelSelectorParam(selector) + if o.Selector != "" { + builder.ResourceTypes("pods").LabelSelectorParam(o.Selector) } infos, err := builder.Do().Infos() if err != nil { return err } - if selector == "" && len(infos) != 1 { + if o.Selector == "" && len(infos) != 1 { return errors.New("expected a resource") } o.Object = infos[0].Object @@ -229,15 +254,36 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []str } func (o LogsOptions) Validate() error { - logsOptions, ok := o.Options.(*api.PodLogOptions) + if o.Follow && len(o.Selector) > 0 { + return fmt.Errorf("only one of follow (-f) or selector (-l) is allowed") + } + + if len(o.SinceTime) > 0 && o.SinceSeconds != 0 { + return fmt.Errorf("at most one of `sinceTime` or `sinceSeconds` may be specified") + } + + logsOptions, ok := o.Options.(*corev1.PodLogOptions) if !ok { return errors.New("unexpected logs options object") } if o.AllContainers && len(logsOptions.Container) > 0 { return fmt.Errorf("--all-containers=true should not be specified with container name %s", logsOptions.Container) } - if errs := validation.ValidatePodLogOptions(logsOptions); len(errs) > 0 { - return errs.ToAggregate() + + if o.ContainerNameSpecified && len(o.Resources) == 2 { + return fmt.Errorf("only one of -c or an inline [CONTAINER] arg is allowed") + } + + if o.LimitBytes < 0 { + return fmt.Errorf("--limit-bytes must be greater than 0") + } + + if logsOptions.SinceSeconds != nil && *logsOptions.SinceSeconds < int64(0) { + return fmt.Errorf("--since must be greater than 0") + } + + if logsOptions.TailLines != nil && *logsOptions.TailLines < 0 { + return fmt.Errorf("TailLines must be greater than or equal to 0") } return nil @@ -251,7 +297,7 @@ func (o LogsOptions) RunLogs() error { } for _, request := range requests { - if err := consumeRequest(request, o.Out); err != nil { + if err := o.ConsumeRequestFn(request, o.Out); err != nil { return err } } diff --git a/pkg/kubectl/cmd/logs_test.go b/pkg/kubectl/cmd/logs_test.go index 03d2dd22a24..934fa5ee8e0 100644 --- a/pkg/kubectl/cmd/logs_test.go +++ b/pkg/kubectl/cmd/logs_test.go @@ -17,29 +17,20 @@ limitations under the License. package cmd import ( - "bytes" "errors" - "io/ioutil" - "net/http" + "fmt" + "io" "strings" "testing" "time" - "github.com/spf13/cobra" - - "fmt" - + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" restclient "k8s.io/client-go/rest" - "k8s.io/client-go/rest/fake" - "k8s.io/kubernetes/pkg/api/legacyscheme" api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" - "k8s.io/kubernetes/pkg/kubectl/polymorphichelpers" - "k8s.io/kubernetes/pkg/kubectl/scheme" ) func TestLog(t *testing.T) { @@ -48,11 +39,8 @@ func TestLog(t *testing.T) { pod *api.Pod }{ { - name: "v1 - pod log", - version: "v1", - podPath: "/namespaces/test/pods/foo", - logPath: "/api/v1/namespaces/test/pods/foo/log", - pod: testPod(), + name: "v1 - pod log", + pod: testPod(), }, } for _, test := range tests { @@ -61,41 +49,19 @@ func TestLog(t *testing.T) { tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) - ns := legacyscheme.Codecs - - tf.Client = &fake.RESTClient{ - NegotiatedSerializer: ns, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == test.podPath && m == "GET": - body := objBody(codec, test.pod) - return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil - case p == test.logPath && m == "GET": - body := ioutil.NopCloser(bytes.NewBufferString(logContent)) - return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil - default: - t.Errorf("%s: unexpected request: %#v\n%#v", test.name, req.URL, req) - return nil, nil - } - }), - } - tf.ClientConfigVal = defaultClientConfig() - oldLogFn := polymorphichelpers.LogsForObjectFn - defer func() { - polymorphichelpers.LogsForObjectFn = oldLogFn - }() - clientset, err := tf.ClientSet() - if err != nil { - t.Fatal(err) - } - polymorphichelpers.LogsForObjectFn = logTestMock{client: clientset}.logsForObject - streams, _, buf, _ := genericclioptions.NewTestIOStreams() - cmd := NewCmdLogs(tf, streams) - cmd.Flags().Set("namespace", "test") - cmd.Run(cmd, []string{"foo"}) + mock := &logTestMock{ + logsContent: logContent, + } + + opts := NewLogsOptions(streams, false) + opts.Namespace = "test" + opts.Object = test.pod + opts.Options = &corev1.PodLogOptions{} + opts.LogsForObject = mock.mockLogsForObject + opts.ConsumeRequestFn = mock.mockConsumeRequest + opts.RunLogs() if buf.String() != logContent { t.Errorf("%s: did not get expected log content. Got: %s", test.name, buf.String()) @@ -119,66 +85,152 @@ func testPod() *api.Pod { } } -func TestValidateLogFlags(t *testing.T) { +func TestValidateLogOptions(t *testing.T) { f := cmdtesting.NewTestFactory() defer f.Cleanup() f.WithNamespace("") tests := []struct { name string - flags map[string]string args []string + opts func(genericclioptions.IOStreams) *LogsOptions expected string }{ { - name: "since & since-time", - flags: map[string]string{"since": "1h", "since-time": "2006-01-02T15:04:05Z"}, + name: "since & since-time", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.SinceSeconds = time.Hour + o.SinceTime = "2006-01-02T15:04:05Z" + + var err error + o.Options, err = o.ToLogOptions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + return o + }, args: []string{"foo"}, expected: "at most one of `sinceTime` or `sinceSeconds` may be specified", }, { - name: "negative since-time", - flags: map[string]string{"since": "-1s"}, + name: "negative since-time", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.SinceSeconds = -1 * time.Second + + var err error + o.Options, err = o.ToLogOptions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + return o + }, args: []string{"foo"}, expected: "must be greater than 0", }, { - name: "negative limit-bytes", - flags: map[string]string{"limit-bytes": "-100"}, + name: "negative limit-bytes", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.LimitBytes = -100 + + var err error + o.Options, err = o.ToLogOptions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + return o + }, args: []string{"foo"}, expected: "must be greater than 0", }, { - name: "negative tail", - flags: map[string]string{"tail": "-100"}, + name: "negative tail", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.Tail = -100 + + var err error + o.Options, err = o.ToLogOptions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + return o + }, args: []string{"foo"}, expected: "must be greater than or equal to 0", }, { - name: "container name combined with --all-containers", - flags: map[string]string{"all-containers": "true"}, + name: "container name combined with --all-containers", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, true) + o.Container = "my-container" + + var err error + o.Options, err = o.ToLogOptions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + return o + }, args: []string{"my-pod", "my-container"}, expected: "--all-containers=true should not be specified with container", }, + { + name: "container name combined with second argument", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.Container = "my-container" + o.ContainerNameSpecified = true + + var err error + o.Options, err = o.ToLogOptions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + return o + }, + args: []string{"my-pod", "my-container"}, + expected: "only one of -c or an inline", + }, + { + name: "follow and selector conflict", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.Selector = "foo" + o.Follow = true + + var err error + o.Options, err = o.ToLogOptions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + return o + }, + expected: "only one of follow (-f) or selector (-l) is allowed", + }, } for _, test := range tests { 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 := NewLogsOptions(streams, test.flags["all-containers"] == "true") - cmd.Run = func(cmd *cobra.Command, args []string) { - o.Complete(f, cmd, args) - out = o.Validate().Error() - } - cmd.Run(cmd, test.args) - 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) + o := test.opts(streams) + o.Resources = test.args + + err := o.Validate() + if err == nil { + t.Fatalf("expected error %q, got none", test.expected) + } + + if !strings.Contains(err.Error(), test.expected) { + t.Errorf("%s: expected to find:\n\t%s\nfound:\n\t%s\n", test.name, test.expected, err.Error()) } } } @@ -190,49 +242,49 @@ func TestLogComplete(t *testing.T) { tests := []struct { name string args []string - flags map[string]string + opts func(genericclioptions.IOStreams) *LogsOptions expected string }{ { - name: "No args case", - flags: map[string]string{"selector": ""}, + name: "No args case", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + return NewLogsOptions(streams, false) + }, expected: "'logs (POD | TYPE/NAME) [CONTAINER_NAME]'.\nPOD or TYPE/NAME is a required argument for the logs command", }, { - name: "One args case", - args: []string{"foo"}, - flags: map[string]string{"selector": "foo"}, + name: "One args case", + args: []string{"foo"}, + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.Selector = "foo" + return o + }, expected: "only a selector (-l) or a POD name is allowed", }, { - name: "Two args case", - args: []string{"foo", "foo1"}, - flags: map[string]string{"container": "foo1"}, - expected: "only one of -c or an inline [CONTAINER] arg is allowed", - }, - { - name: "More than two args case", - args: []string{"foo", "foo1", "foo2"}, - flags: map[string]string{"tail": "1"}, + name: "More than two args case", + args: []string{"foo", "foo1", "foo2"}, + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.Tail = 1 + return o + }, expected: "'logs (POD | TYPE/NAME) [CONTAINER_NAME]'.\nPOD or TYPE/NAME is a required argument for the logs command", }, - { - name: "follow and selecter conflict", - flags: map[string]string{"selector": "foo", "follow": "true"}, - expected: "only one of follow (-f) or selector (-l) is allowed", - }, } for _, test := range tests { cmd := NewCmdLogs(f, genericclioptions.NewTestIOStreamsDiscard()) - var err error 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 := NewLogsOptions(genericclioptions.NewTestIOStreamsDiscard(), false) - err = o.Complete(f, cmd, test.args) + o := test.opts(genericclioptions.NewTestIOStreamsDiscard()) + err := o.Complete(f, cmd, test.args) + if err == nil { + t.Fatalf("expected error %q, got none", test.expected) + } + 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) @@ -241,17 +293,23 @@ func TestLogComplete(t *testing.T) { } type logTestMock struct { - client internalclientset.Interface + logsContent string } -func (m logTestMock) logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*restclient.Request, error) { - switch t := object.(type) { +func (l *logTestMock) mockConsumeRequest(req *restclient.Request, out io.Writer) error { + fmt.Fprintf(out, l.logsContent) + return nil +} + +func (l *logTestMock) mockLogsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*restclient.Request, error) { + switch object.(type) { case *api.Pod: - opts, ok := options.(*api.PodLogOptions) + _, ok := options.(*corev1.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } - return []*restclient.Request{m.client.Core().Pods(t.Namespace).GetLogs(t.Name, opts)}, nil + + return []*restclient.Request{{}}, nil default: return nil, fmt.Errorf("cannot get the logs from %T", object) }