fix logs command to be generic for all resources again

pull/8/head
David Eads 2018-07-19 16:06:50 -04:00
parent 8770d12494
commit 5ba07364ee
7 changed files with 113 additions and 70 deletions

View File

@ -236,13 +236,14 @@ func (o *ClusterInfoDumpOptions) Run() error {
writer.Write([]byte(fmt.Sprintf("==== START logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))
defer writer.Write([]byte(fmt.Sprintf("==== END logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))
request, err := o.LogsForObject(o.RESTClientGetter, pod, &api.PodLogOptions{Container: container.Name}, timeout)
requests, err := o.LogsForObject(o.RESTClientGetter, pod, &api.PodLogOptions{Container: container.Name}, timeout, false)
if err != nil {
// Print error and return.
writer.Write([]byte(fmt.Sprintf("Create log request error: %s\n", err.Error())))
return
}
for _, request := range requests {
data, err := request.DoRaw()
if err != nil {
// Print error and return.
@ -251,6 +252,7 @@ func (o *ClusterInfoDumpOptions) Run() error {
}
writer.Write(data)
}
}
for ix := range pods.Items {
pod := &pods.Items[ix]

View File

@ -27,6 +27,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/validation"
@ -244,55 +245,27 @@ func (o LogsOptions) Validate() error {
// RunLogs retrieves a pod log
func (o LogsOptions) RunLogs() error {
switch t := o.Object.(type) {
case *api.PodList:
for _, p := range t.Items {
if err := o.getPodLogs(&p); err != nil {
return err
}
}
return nil
case *api.Pod:
return o.getPodLogs(t)
default:
return o.getLogs(o.Object)
}
}
// getPodLogs checks whether o.AllContainers is set to true.
// If so, it retrives all containers' log in the pod.
func (o LogsOptions) getPodLogs(pod *api.Pod) error {
if !o.AllContainers {
return o.getLogs(pod)
}
for _, c := range pod.Spec.InitContainers {
o.Options.(*api.PodLogOptions).Container = c.Name
if err := o.getLogs(pod); err != nil {
return err
}
}
for _, c := range pod.Spec.Containers {
o.Options.(*api.PodLogOptions).Container = c.Name
if err := o.getLogs(pod); err != nil {
return err
}
}
return nil
}
func (o LogsOptions) getLogs(obj runtime.Object) error {
req, err := o.LogsForObject(o.RESTClientGetter, obj, o.Options, o.GetPodTimeout)
requests, err := o.LogsForObject(o.RESTClientGetter, o.Object, o.Options, o.GetPodTimeout, o.AllContainers)
if err != nil {
return err
}
readCloser, err := req.Stream()
for _, request := range requests {
if err := consumeRequest(request, o.Out); err != nil {
return err
}
}
return nil
}
func consumeRequest(request *rest.Request, out io.Writer) error {
readCloser, err := request.Stream()
if err != nil {
return err
}
defer readCloser.Close()
_, err = io.Copy(o.Out, readCloser)
_, err = io.Copy(out, readCloser)
return err
}

View File

@ -244,14 +244,14 @@ type logTestMock struct {
client internalclientset.Interface
}
func (m logTestMock) logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration) (*restclient.Request, error) {
func (m logTestMock) logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*restclient.Request, error) {
switch t := object.(type) {
case *api.Pod:
opts, ok := options.(*api.PodLogOptions)
if !ok {
return nil, errors.New("provided options object is not a PodLogOptions")
}
return m.client.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil
return []*restclient.Request{m.client.Core().Pods(t.Namespace).GetLogs(t.Name, opts)}, nil
default:
return nil, fmt.Errorf("cannot get the logs from %T", object)
}

View File

@ -18,7 +18,6 @@ package cmd
import (
"fmt"
"io"
"github.com/docker/distribution/reference"
"github.com/spf13/cobra"
@ -523,21 +522,16 @@ func logOpts(restClientGetter genericclioptions.RESTClientGetter, pod *api.Pod,
return err
}
req, err := polymorphichelpers.LogsForObjectFn(restClientGetter, pod, &api.PodLogOptions{Container: ctrName}, opts.GetPodTimeout)
requests, err := polymorphichelpers.LogsForObjectFn(restClientGetter, pod, &api.PodLogOptions{Container: ctrName}, opts.GetPodTimeout, false)
if err != nil {
return err
}
for _, request := range requests {
if err := consumeRequest(request, opts.Out); err != nil {
return err
}
}
readCloser, err := req.Stream()
if err != nil {
return err
}
defer readCloser.Close()
_, err = io.Copy(opts.Out, readCloser)
if err != nil {
return err
}
return nil
}

View File

@ -30,7 +30,7 @@ import (
)
// LogsForObjectFunc is a function type that can tell you how to get logs for a runtime.object
type LogsForObjectFunc func(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration) (*rest.Request, error)
type LogsForObjectFunc func(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*rest.Request, error)
// LogsForObjectFn gives a way to easily override the function for unit testing if needed.
var LogsForObjectFn LogsForObjectFunc = logsForObject

View File

@ -33,7 +33,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
func logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration) (*rest.Request, error) {
func logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*rest.Request, error) {
clientConfig, err := restClientGetter.ToRESTConfig()
if err != nil {
return nil, err
@ -42,21 +42,94 @@ func logsForObject(restClientGetter genericclioptions.RESTClientGetter, object,
if err != nil {
return nil, err
}
return logsForObjectWithClient(clientset, object, options, timeout)
return logsForObjectWithClient(clientset, object, options, timeout, allContainers)
}
// this is split for easy test-ability
func logsForObjectWithClient(clientset internalclientset.Interface, object, options runtime.Object, timeout time.Duration) (*rest.Request, error) {
func logsForObjectWithClient(clientset internalclientset.Interface, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*rest.Request, error) {
opts, ok := options.(*coreinternal.PodLogOptions)
if !ok {
return nil, errors.New("provided options object is not a PodLogOptions")
}
switch t := object.(type) {
case *coreinternal.PodList:
ret := []*rest.Request{}
for i := range t.Items {
currRet, err := logsForObjectWithClient(clientset, &t.Items[i], options, timeout, allContainers)
if err != nil {
return nil, err
}
ret = append(ret, currRet...)
}
return ret, nil
case *corev1.PodList:
ret := []*rest.Request{}
for i := range t.Items {
currRet, err := logsForObjectWithClient(clientset, &t.Items[i], options, timeout, allContainers)
if err != nil {
return nil, err
}
ret = append(ret, currRet...)
}
return ret, nil
case *coreinternal.Pod:
return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil
// if allContainers is true, then we're going to locate all containers and then iterate through them. At that point, "allContainers" is false
if !allContainers {
return []*rest.Request{clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts)}, nil
}
ret := []*rest.Request{}
for _, c := range t.Spec.InitContainers {
currOpts := opts.DeepCopy()
currOpts.Container = c.Name
currRet, err := logsForObjectWithClient(clientset, t, options, timeout, false)
if err != nil {
return nil, err
}
ret = append(ret, currRet...)
}
for _, c := range t.Spec.Containers {
currOpts := opts.DeepCopy()
currOpts.Container = c.Name
currRet, err := logsForObjectWithClient(clientset, t, options, timeout, false)
if err != nil {
return nil, err
}
ret = append(ret, currRet...)
}
return ret, nil
case *corev1.Pod:
return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil
// if allContainers is true, then we're going to locate all containers and then iterate through them. At that point, "allContainers" is false
if !allContainers {
return []*rest.Request{clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts)}, nil
}
ret := []*rest.Request{}
for _, c := range t.Spec.InitContainers {
currOpts := opts.DeepCopy()
currOpts.Container = c.Name
currRet, err := logsForObjectWithClient(clientset, t, options, timeout, false)
if err != nil {
return nil, err
}
ret = append(ret, currRet...)
}
for _, c := range t.Spec.Containers {
currOpts := opts.DeepCopy()
currOpts.Container = c.Name
currRet, err := logsForObjectWithClient(clientset, t, options, timeout, false)
if err != nil {
return nil, err
}
ret = append(ret, currRet...)
}
return ret, nil
}
namespace, selector, err := SelectorsForObject(object)
@ -71,5 +144,6 @@ func logsForObjectWithClient(clientset internalclientset.Interface, object, opti
if numPods > 1 {
fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name)
}
return clientset.Core().Pods(pod.Namespace).GetLogs(pod.Name, opts), nil
return logsForObjectWithClient(clientset, pod, options, timeout, allContainers)
}

View File

@ -130,7 +130,7 @@ func TestLogsForObject(t *testing.T) {
for _, test := range tests {
fakeClientset := fake.NewSimpleClientset(test.pods...)
_, err := logsForObjectWithClient(fakeClientset, test.obj, test.opts, 20*time.Second)
_, err := logsForObjectWithClient(fakeClientset, test.obj, test.opts, 20*time.Second, false)
if err != nil {
t.Errorf("%s: unexpected error: %v", test.name, err)
continue