...

Source file src/k8s.io/kubectl/pkg/cmd/clusterinfo/clusterinfo_dump.go

Documentation: k8s.io/kubectl/pkg/cmd/clusterinfo

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package clusterinfo
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  	"os"
    24  	"path"
    25  	"time"
    26  
    27  	"github.com/spf13/cobra"
    28  
    29  	corev1 "k8s.io/api/core/v1"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/cli-runtime/pkg/genericclioptions"
    32  	"k8s.io/cli-runtime/pkg/genericiooptions"
    33  	"k8s.io/cli-runtime/pkg/printers"
    34  	appsv1client "k8s.io/client-go/kubernetes/typed/apps/v1"
    35  	corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
    36  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    37  	"k8s.io/kubectl/pkg/polymorphichelpers"
    38  	"k8s.io/kubectl/pkg/scheme"
    39  	"k8s.io/kubectl/pkg/util/i18n"
    40  	"k8s.io/kubectl/pkg/util/templates"
    41  )
    42  
    43  const (
    44  	defaultPodLogsTimeout = 20 * time.Second
    45  	timeout               = 5 * time.Minute
    46  )
    47  
    48  type ClusterInfoDumpOptions struct {
    49  	PrintFlags *genericclioptions.PrintFlags
    50  	PrintObj   printers.ResourcePrinterFunc
    51  
    52  	OutputDir     string
    53  	AllNamespaces bool
    54  	Namespaces    []string
    55  
    56  	Timeout          time.Duration
    57  	AppsClient       appsv1client.AppsV1Interface
    58  	CoreClient       corev1client.CoreV1Interface
    59  	Namespace        string
    60  	RESTClientGetter genericclioptions.RESTClientGetter
    61  	LogsForObject    polymorphichelpers.LogsForObjectFunc
    62  
    63  	genericiooptions.IOStreams
    64  }
    65  
    66  func NewCmdClusterInfoDump(restClientGetter genericclioptions.RESTClientGetter, ioStreams genericiooptions.IOStreams) *cobra.Command {
    67  	o := &ClusterInfoDumpOptions{
    68  		PrintFlags: genericclioptions.NewPrintFlags("").WithTypeSetter(scheme.Scheme).WithDefaultOutput("json"),
    69  
    70  		IOStreams: ioStreams,
    71  	}
    72  
    73  	cmd := &cobra.Command{
    74  		Use:     "dump",
    75  		Short:   i18n.T("Dump relevant information for debugging and diagnosis"),
    76  		Long:    dumpLong,
    77  		Example: dumpExample,
    78  		Run: func(cmd *cobra.Command, args []string) {
    79  			cmdutil.CheckErr(o.Complete(restClientGetter, cmd))
    80  			cmdutil.CheckErr(o.Run())
    81  		},
    82  	}
    83  
    84  	o.PrintFlags.AddFlags(cmd)
    85  
    86  	cmd.Flags().StringVar(&o.OutputDir, "output-directory", o.OutputDir, i18n.T("Where to output the files.  If empty or '-' uses stdout, otherwise creates a directory hierarchy in that directory"))
    87  	cmd.Flags().StringSliceVar(&o.Namespaces, "namespaces", o.Namespaces, "A comma separated list of namespaces to dump.")
    88  	cmd.Flags().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If true, dump all namespaces.  If true, --namespaces is ignored.")
    89  	cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodLogsTimeout)
    90  	return cmd
    91  }
    92  
    93  var (
    94  	dumpLong = templates.LongDesc(i18n.T(`
    95      Dump cluster information out suitable for debugging and diagnosing cluster problems.  By default, dumps everything to
    96      stdout. You can optionally specify a directory with --output-directory.  If you specify a directory, Kubernetes will
    97      build a set of files in that directory.  By default, only dumps things in the current namespace and 'kube-system' namespace, but you can
    98      switch to a different namespace with the --namespaces flag, or specify --all-namespaces to dump all namespaces.
    99  
   100      The command also dumps the logs of all of the pods in the cluster; these logs are dumped into different directories
   101      based on namespace and pod name.`))
   102  
   103  	dumpExample = templates.Examples(i18n.T(`
   104      # Dump current cluster state to stdout
   105      kubectl cluster-info dump
   106  
   107      # Dump current cluster state to /path/to/cluster-state
   108      kubectl cluster-info dump --output-directory=/path/to/cluster-state
   109  
   110      # Dump all namespaces to stdout
   111      kubectl cluster-info dump --all-namespaces
   112  
   113      # Dump a set of namespaces to /path/to/cluster-state
   114      kubectl cluster-info dump --namespaces default,kube-system --output-directory=/path/to/cluster-state`))
   115  )
   116  
   117  func setupOutputWriter(dir string, defaultWriter io.Writer, filename string, fileExtension string) io.Writer {
   118  	if len(dir) == 0 || dir == "-" {
   119  		return defaultWriter
   120  	}
   121  	fullFile := path.Join(dir, filename) + fileExtension
   122  	parent := path.Dir(fullFile)
   123  	cmdutil.CheckErr(os.MkdirAll(parent, 0755))
   124  
   125  	file, err := os.Create(fullFile)
   126  	cmdutil.CheckErr(err)
   127  	return file
   128  }
   129  
   130  func (o *ClusterInfoDumpOptions) Complete(restClientGetter genericclioptions.RESTClientGetter, cmd *cobra.Command) error {
   131  	printer, err := o.PrintFlags.ToPrinter()
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	o.PrintObj = printer.PrintObj
   137  
   138  	config, err := restClientGetter.ToRESTConfig()
   139  	if err != nil {
   140  		return err
   141  	}
   142  
   143  	o.CoreClient, err = corev1client.NewForConfig(config)
   144  	if err != nil {
   145  		return err
   146  	}
   147  
   148  	o.AppsClient, err = appsv1client.NewForConfig(config)
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	o.Timeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd)
   154  	if err != nil {
   155  		return err
   156  	}
   157  
   158  	o.Namespace, _, err = restClientGetter.ToRawKubeConfigLoader().Namespace()
   159  	if err != nil {
   160  		return err
   161  	}
   162  
   163  	o.RESTClientGetter = restClientGetter
   164  	o.LogsForObject = polymorphichelpers.LogsForObjectFn
   165  
   166  	return nil
   167  }
   168  
   169  func (o *ClusterInfoDumpOptions) Run() error {
   170  	nodes, err := o.CoreClient.Nodes().List(context.TODO(), metav1.ListOptions{})
   171  	if err != nil {
   172  		return err
   173  	}
   174  
   175  	fileExtension := ".txt"
   176  	if o.PrintFlags.OutputFormat != nil {
   177  		switch *o.PrintFlags.OutputFormat {
   178  		case "json":
   179  			fileExtension = ".json"
   180  		case "yaml":
   181  			fileExtension = ".yaml"
   182  		}
   183  	}
   184  
   185  	if err := o.PrintObj(nodes, setupOutputWriter(o.OutputDir, o.Out, "nodes", fileExtension)); err != nil {
   186  		return err
   187  	}
   188  
   189  	var namespaces []string
   190  	if o.AllNamespaces {
   191  		namespaceList, err := o.CoreClient.Namespaces().List(context.TODO(), metav1.ListOptions{})
   192  		if err != nil {
   193  			return err
   194  		}
   195  		for ix := range namespaceList.Items {
   196  			namespaces = append(namespaces, namespaceList.Items[ix].Name)
   197  		}
   198  	} else {
   199  		if len(o.Namespaces) == 0 {
   200  			namespaces = []string{
   201  				metav1.NamespaceSystem,
   202  				o.Namespace,
   203  			}
   204  		} else {
   205  			namespaces = o.Namespaces
   206  		}
   207  	}
   208  	for _, namespace := range namespaces {
   209  		// TODO: this is repetitive in the extreme.  Use reflection or
   210  		// something to make this a for loop.
   211  		events, err := o.CoreClient.Events(namespace).List(context.TODO(), metav1.ListOptions{})
   212  		if err != nil {
   213  			return err
   214  		}
   215  		if err := o.PrintObj(events, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "events"), fileExtension)); err != nil {
   216  			return err
   217  		}
   218  
   219  		rcs, err := o.CoreClient.ReplicationControllers(namespace).List(context.TODO(), metav1.ListOptions{})
   220  		if err != nil {
   221  			return err
   222  		}
   223  		if err := o.PrintObj(rcs, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "replication-controllers"), fileExtension)); err != nil {
   224  			return err
   225  		}
   226  
   227  		svcs, err := o.CoreClient.Services(namespace).List(context.TODO(), metav1.ListOptions{})
   228  		if err != nil {
   229  			return err
   230  		}
   231  		if err := o.PrintObj(svcs, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "services"), fileExtension)); err != nil {
   232  			return err
   233  		}
   234  
   235  		sets, err := o.AppsClient.DaemonSets(namespace).List(context.TODO(), metav1.ListOptions{})
   236  		if err != nil {
   237  			return err
   238  		}
   239  		if err := o.PrintObj(sets, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "daemonsets"), fileExtension)); err != nil {
   240  			return err
   241  		}
   242  
   243  		deps, err := o.AppsClient.Deployments(namespace).List(context.TODO(), metav1.ListOptions{})
   244  		if err != nil {
   245  			return err
   246  		}
   247  		if err := o.PrintObj(deps, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "deployments"), fileExtension)); err != nil {
   248  			return err
   249  		}
   250  
   251  		rps, err := o.AppsClient.ReplicaSets(namespace).List(context.TODO(), metav1.ListOptions{})
   252  		if err != nil {
   253  			return err
   254  		}
   255  		if err := o.PrintObj(rps, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "replicasets"), fileExtension)); err != nil {
   256  			return err
   257  		}
   258  
   259  		pods, err := o.CoreClient.Pods(namespace).List(context.TODO(), metav1.ListOptions{})
   260  		if err != nil {
   261  			return err
   262  		}
   263  
   264  		if err := o.PrintObj(pods, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "pods"), fileExtension)); err != nil {
   265  			return err
   266  		}
   267  
   268  		printContainer := func(writer io.Writer, container corev1.Container, pod *corev1.Pod) {
   269  			writer.Write([]byte(fmt.Sprintf("==== START logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))
   270  			defer writer.Write([]byte(fmt.Sprintf("==== END logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))
   271  
   272  			requests, err := o.LogsForObject(o.RESTClientGetter, pod, &corev1.PodLogOptions{Container: container.Name}, timeout, false)
   273  			if err != nil {
   274  				// Print error and return.
   275  				writer.Write([]byte(fmt.Sprintf("Create log request error: %s\n", err.Error())))
   276  				return
   277  			}
   278  
   279  			for _, request := range requests {
   280  				data, err := request.DoRaw(context.TODO())
   281  				if err != nil {
   282  					// Print error and return.
   283  					writer.Write([]byte(fmt.Sprintf("Request log error: %s\n", err.Error())))
   284  					return
   285  				}
   286  				writer.Write(data)
   287  			}
   288  		}
   289  
   290  		for ix := range pods.Items {
   291  			pod := &pods.Items[ix]
   292  			initcontainers := pod.Spec.InitContainers
   293  			containers := pod.Spec.Containers
   294  			writer := setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, pod.Name, "logs"), ".txt")
   295  
   296  			for i := range initcontainers {
   297  				printContainer(writer, initcontainers[i], pod)
   298  			}
   299  			for i := range containers {
   300  				printContainer(writer, containers[i], pod)
   301  			}
   302  		}
   303  	}
   304  
   305  	dest := o.OutputDir
   306  	if len(dest) > 0 && dest != "-" {
   307  		fmt.Fprintf(o.Out, "Cluster info dumped to %s\n", dest)
   308  	}
   309  	return nil
   310  }
   311  

View as plain text