2016-02-04 22:12:05 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2016 The Kubernetes Authors .
2016-02-04 22:12:05 +00:00
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 .
* /
2018-10-05 11:06:12 +00:00
package clusterinfo
2016-02-04 22:12:05 +00:00
import (
"fmt"
"io"
"os"
"path"
2018-04-26 13:30:21 +00:00
"time"
2016-02-04 22:12:05 +00:00
"github.com/spf13/cobra"
2018-08-02 18:24:22 +00:00
corev1 "k8s.io/api/core/v1"
2017-01-22 03:36:02 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2018-08-21 10:46:39 +00:00
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericclioptions/printers"
2018-08-02 04:14:44 +00:00
appsv1client "k8s.io/client-go/kubernetes/typed/apps/v1"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
2016-02-04 22:12:05 +00:00
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
2018-05-16 14:47:29 +00:00
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
2018-05-16 18:19:21 +00:00
"k8s.io/kubernetes/pkg/kubectl/scheme"
2017-07-07 04:04:11 +00:00
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
2018-10-10 18:29:30 +00:00
"k8s.io/kubernetes/pkg/kubectl/util/templates"
2016-02-04 22:12:05 +00:00
)
2018-10-05 12:38:38 +00:00
const (
defaultPodLogsTimeout = 20 * time . Second
timeout = 5 * time . Minute
)
2018-04-19 00:02:37 +00:00
type ClusterInfoDumpOptions struct {
2018-05-02 19:15:47 +00:00
PrintFlags * genericclioptions . PrintFlags
2018-04-19 00:02:37 +00:00
PrintObj printers . ResourcePrinterFunc
2018-04-26 13:30:21 +00:00
OutputDir string
AllNamespaces bool
Namespaces [ ] string
2018-05-16 14:47:29 +00:00
Timeout time . Duration
2018-08-02 04:14:44 +00:00
AppsClient appsv1client . AppsV1Interface
CoreClient corev1client . CoreV1Interface
2018-05-16 14:47:29 +00:00
Namespace string
RESTClientGetter genericclioptions . RESTClientGetter
LogsForObject polymorphichelpers . LogsForObjectFunc
2018-04-26 13:30:21 +00:00
2018-04-19 00:02:37 +00:00
genericclioptions . IOStreams
}
2016-02-04 22:12:05 +00:00
// NewCmdCreateSecret groups subcommands to create various types of secrets
2018-04-19 00:02:37 +00:00
func NewCmdClusterInfoDump ( f cmdutil . Factory , ioStreams genericclioptions . IOStreams ) * cobra . Command {
o := & ClusterInfoDumpOptions {
2018-08-02 04:14:44 +00:00
PrintFlags : genericclioptions . NewPrintFlags ( "" ) . WithTypeSetter ( scheme . Scheme ) . WithDefaultOutput ( "json" ) ,
2018-04-19 00:02:37 +00:00
IOStreams : ioStreams ,
}
2016-02-04 22:12:05 +00:00
cmd := & cobra . Command {
2016-05-16 23:50:13 +00:00
Use : "dump" ,
2017-01-25 01:00:32 +00:00
Short : i18n . T ( "Dump lots of relevant info for debugging and diagnosis" ) ,
2016-05-16 23:50:13 +00:00
Long : dumpLong ,
Example : dumpExample ,
2016-02-04 22:12:05 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2018-04-26 13:30:21 +00:00
cmdutil . CheckErr ( o . Complete ( f , cmd ) )
cmdutil . CheckErr ( o . Run ( ) )
2016-02-04 22:12:05 +00:00
} ,
}
2018-08-02 04:14:44 +00:00
o . PrintFlags . AddFlags ( cmd )
2018-05-10 08:48:37 +00:00
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" ) )
cmd . Flags ( ) . StringSliceVar ( & o . Namespaces , "namespaces" , o . Namespaces , "A comma separated list of namespaces to dump." )
cmd . Flags ( ) . BoolVar ( & o . AllNamespaces , "all-namespaces" , o . AllNamespaces , "If true, dump all namespaces. If true, --namespaces is ignored." )
2017-02-21 16:19:13 +00:00
cmdutil . AddPodRunningTimeoutFlag ( cmd , defaultPodLogsTimeout )
2016-02-04 22:12:05 +00:00
return cmd
}
2016-10-07 22:24:42 +00:00
var (
2017-03-15 03:49:10 +00:00
dumpLong = templates . LongDesc ( i18n . T ( `
2016-10-07 22:24:42 +00:00
Dumps cluster info out suitable for debugging and diagnosing cluster problems . By default , dumps everything to
stdout . You can optionally specify a directory with -- output - directory . If you specify a directory , kubernetes will
build a set of files in that directory . By default only dumps things in the ' kube - system ' namespace , but you can
switch to a different namespace with the -- namespaces flag , or specify -- all - namespaces to dump all namespaces .
2016-05-11 18:17:26 +00:00
2016-10-07 22:24:42 +00:00
The command also dumps the logs of all of the pods in the cluster , these logs are dumped into different directories
2017-03-15 03:49:10 +00:00
based on namespace and pod name . ` ) )
2016-02-04 22:12:05 +00:00
2017-03-15 03:49:10 +00:00
dumpExample = templates . Examples ( i18n . T ( `
2016-10-07 22:24:42 +00:00
# Dump current cluster state to stdout
kubectl cluster - info dump
2016-06-01 12:50:29 +00:00
2016-10-07 22:24:42 +00:00
# Dump current cluster state to / path / to / cluster - state
kubectl cluster - info dump -- output - directory = / path / to / cluster - state
2016-06-01 12:50:29 +00:00
2016-10-07 22:24:42 +00:00
# Dump all namespaces to stdout
kubectl cluster - info dump -- all - namespaces
2016-06-01 12:50:29 +00:00
2016-10-07 22:24:42 +00:00
# Dump a set of namespaces to / path / to / cluster - state
2017-03-15 03:49:10 +00:00
kubectl cluster - info dump -- namespaces default , kube - system -- output - directory = / path / to / cluster - state ` ) )
2016-02-04 22:12:05 +00:00
)
2018-04-26 13:30:21 +00:00
func setupOutputWriter ( dir string , defaultWriter io . Writer , filename string ) io . Writer {
2016-02-04 22:12:05 +00:00
if len ( dir ) == 0 || dir == "-" {
2016-03-25 16:25:24 +00:00
return defaultWriter
2016-02-04 22:12:05 +00:00
}
fullFile := path . Join ( dir , filename )
parent := path . Dir ( fullFile )
cmdutil . CheckErr ( os . MkdirAll ( parent , 0755 ) )
file , err := os . Create ( path . Join ( dir , filename ) )
cmdutil . CheckErr ( err )
return file
}
2018-04-26 13:30:21 +00:00
func ( o * ClusterInfoDumpOptions ) Complete ( f cmdutil . Factory , cmd * cobra . Command ) error {
2018-04-19 00:02:37 +00:00
printer , err := o . PrintFlags . ToPrinter ( )
if err != nil {
return err
}
o . PrintObj = printer . PrintObj
2018-08-02 04:14:44 +00:00
config , err := f . ToRESTConfig ( )
if err != nil {
return err
}
o . CoreClient , err = corev1client . NewForConfig ( config )
if err != nil {
return err
}
o . AppsClient , err = appsv1client . NewForConfig ( config )
2017-02-21 16:19:13 +00:00
if err != nil {
2018-04-26 13:30:21 +00:00
return err
2017-02-21 16:19:13 +00:00
}
2018-08-02 04:14:44 +00:00
o . Timeout , err = cmdutil . GetPodRunningTimeoutFlag ( cmd )
2018-04-26 13:30:21 +00:00
if err != nil {
return err
}
2018-08-02 04:14:44 +00:00
2018-05-24 13:33:36 +00:00
o . Namespace , _ , err = f . ToRawKubeConfigLoader ( ) . Namespace ( )
2016-09-07 20:29:57 +00:00
if err != nil {
2016-02-04 22:12:05 +00:00
return err
}
2018-05-16 14:47:29 +00:00
// TODO this should eventually just be the completed kubeconfigflag struct
o . RESTClientGetter = f
o . LogsForObject = polymorphichelpers . LogsForObjectFn
2018-04-26 13:30:21 +00:00
return nil
}
2016-09-07 20:29:57 +00:00
2018-04-26 13:30:21 +00:00
func ( o * ClusterInfoDumpOptions ) Run ( ) error {
2018-08-02 04:14:44 +00:00
nodes , err := o . CoreClient . Nodes ( ) . List ( metav1 . ListOptions { } )
2016-02-04 22:12:05 +00:00
if err != nil {
return err
}
2018-04-26 13:30:21 +00:00
if err := o . PrintObj ( nodes , setupOutputWriter ( o . OutputDir , o . Out , "nodes.json" ) ) ; err != nil {
2016-02-04 22:12:05 +00:00
return err
}
var namespaces [ ] string
2018-04-26 13:30:21 +00:00
if o . AllNamespaces {
2018-08-02 04:14:44 +00:00
namespaceList , err := o . CoreClient . Namespaces ( ) . List ( metav1 . ListOptions { } )
2016-02-04 22:12:05 +00:00
if err != nil {
return err
}
for ix := range namespaceList . Items {
namespaces = append ( namespaces , namespaceList . Items [ ix ] . Name )
}
} else {
2018-04-26 13:30:21 +00:00
if len ( o . Namespaces ) == 0 {
2016-02-04 22:12:05 +00:00
namespaces = [ ] string {
2017-01-22 03:36:02 +00:00
metav1 . NamespaceSystem ,
2018-05-16 14:47:29 +00:00
o . Namespace ,
2016-02-04 22:12:05 +00:00
}
}
}
for _ , namespace := range namespaces {
// TODO: this is repetitive in the extreme. Use reflection or
// something to make this a for loop.
2018-08-02 04:14:44 +00:00
events , err := o . CoreClient . Events ( namespace ) . List ( metav1 . ListOptions { } )
2016-02-04 22:12:05 +00:00
if err != nil {
return err
}
2018-04-26 13:30:21 +00:00
if err := o . PrintObj ( events , setupOutputWriter ( o . OutputDir , o . Out , path . Join ( namespace , "events.json" ) ) ) ; err != nil {
2016-02-04 22:12:05 +00:00
return err
}
2018-08-02 04:14:44 +00:00
rcs , err := o . CoreClient . ReplicationControllers ( namespace ) . List ( metav1 . ListOptions { } )
2016-02-04 22:12:05 +00:00
if err != nil {
return err
}
2018-04-26 13:30:21 +00:00
if err := o . PrintObj ( rcs , setupOutputWriter ( o . OutputDir , o . Out , path . Join ( namespace , "replication-controllers.json" ) ) ) ; err != nil {
2016-02-04 22:12:05 +00:00
return err
}
2018-08-02 04:14:44 +00:00
svcs , err := o . CoreClient . Services ( namespace ) . List ( metav1 . ListOptions { } )
2016-02-04 22:12:05 +00:00
if err != nil {
return err
}
2018-04-26 13:30:21 +00:00
if err := o . PrintObj ( svcs , setupOutputWriter ( o . OutputDir , o . Out , path . Join ( namespace , "services.json" ) ) ) ; err != nil {
2016-02-04 22:12:05 +00:00
return err
}
2018-08-02 04:14:44 +00:00
sets , err := o . AppsClient . DaemonSets ( namespace ) . List ( metav1 . ListOptions { } )
2016-02-04 22:12:05 +00:00
if err != nil {
return err
}
2018-04-26 13:30:21 +00:00
if err := o . PrintObj ( sets , setupOutputWriter ( o . OutputDir , o . Out , path . Join ( namespace , "daemonsets.json" ) ) ) ; err != nil {
2016-02-04 22:12:05 +00:00
return err
}
2018-08-02 04:14:44 +00:00
deps , err := o . AppsClient . Deployments ( namespace ) . List ( metav1 . ListOptions { } )
2016-02-04 22:12:05 +00:00
if err != nil {
return err
}
2018-04-26 13:30:21 +00:00
if err := o . PrintObj ( deps , setupOutputWriter ( o . OutputDir , o . Out , path . Join ( namespace , "deployments.json" ) ) ) ; err != nil {
2016-02-04 22:12:05 +00:00
return err
}
2018-08-02 04:14:44 +00:00
rps , err := o . AppsClient . ReplicaSets ( namespace ) . List ( metav1 . ListOptions { } )
2016-02-04 22:12:05 +00:00
if err != nil {
return err
}
2018-04-26 13:30:21 +00:00
if err := o . PrintObj ( rps , setupOutputWriter ( o . OutputDir , o . Out , path . Join ( namespace , "replicasets.json" ) ) ) ; err != nil {
2016-02-04 22:12:05 +00:00
return err
}
2018-08-02 04:14:44 +00:00
pods , err := o . CoreClient . Pods ( namespace ) . List ( metav1 . ListOptions { } )
2016-02-04 22:12:05 +00:00
if err != nil {
return err
}
2018-04-26 13:30:21 +00:00
if err := o . PrintObj ( pods , setupOutputWriter ( o . OutputDir , o . Out , path . Join ( namespace , "pods.json" ) ) ) ; err != nil {
2016-02-04 22:12:05 +00:00
return err
}
2018-08-02 18:24:22 +00:00
printContainer := func ( writer io . Writer , container corev1 . Container , pod * corev1 . Pod ) {
2017-04-05 08:37:35 +00:00
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 ) ) )
2018-09-13 21:16:07 +00:00
requests , err := o . LogsForObject ( o . RESTClientGetter , pod , & corev1 . PodLogOptions { Container : container . Name } , timeout , false )
2016-02-04 22:12:05 +00:00
if err != nil {
2017-04-05 08:37:35 +00:00
// Print error and return.
writer . Write ( [ ] byte ( fmt . Sprintf ( "Create log request error: %s\n" , err . Error ( ) ) ) )
return
2016-02-04 22:12:05 +00:00
}
2018-07-19 20:06:50 +00:00
for _ , request := range requests {
data , err := request . DoRaw ( )
if err != nil {
// Print error and return.
writer . Write ( [ ] byte ( fmt . Sprintf ( "Request log error: %s\n" , err . Error ( ) ) ) )
return
}
writer . Write ( data )
2016-02-04 22:12:05 +00:00
}
2017-04-05 08:37:35 +00:00
}
for ix := range pods . Items {
pod := & pods . Items [ ix ]
containers := pod . Spec . Containers
2018-04-26 13:30:21 +00:00
writer := setupOutputWriter ( o . OutputDir , o . Out , path . Join ( namespace , pod . Name , "logs.txt" ) )
2017-04-05 08:37:35 +00:00
for i := range containers {
printContainer ( writer , containers [ i ] , pod )
}
2016-02-04 22:12:05 +00:00
}
}
2018-08-02 04:14:44 +00:00
dest := o . OutputDir
if len ( dest ) == 0 {
dest = "standard output"
}
if dest != "-" {
fmt . Fprintf ( o . Out , "Cluster info dumped to %s\n" , dest )
2016-05-11 18:17:26 +00:00
}
2016-02-04 22:12:05 +00:00
return nil
}