From 3c9fb1b651aa0b076d1310fc2c82e8f624413cb8 Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Thu, 4 Feb 2016 14:12:05 -0800 Subject: [PATCH] Add a 'kubectl clusterinfo dump' option --- .generated_docs | 2 + contrib/completions/bash/kubectl | 47 ++++ docs/man/man1/kubectl-cluster-info.1 | 2 +- .../kubectl/kubectl_cluster-info.md | 3 +- docs/yaml/kubectl/kubectl_cluster-info.yaml | 1 + pkg/kubectl/cmd/clusterinfo.go | 1 + pkg/kubectl/cmd/clusterinfo_dump.go | 204 ++++++++++++++++++ 7 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 pkg/kubectl/cmd/clusterinfo_dump.go diff --git a/.generated_docs b/.generated_docs index 5a9c70fc215..2ae3910be6b 100644 --- a/.generated_docs +++ b/.generated_docs @@ -10,6 +10,7 @@ docs/man/man1/kubectl-api-versions.1 docs/man/man1/kubectl-apply.1 docs/man/man1/kubectl-attach.1 docs/man/man1/kubectl-autoscale.1 +docs/man/man1/kubectl-cluster-info-dump.1 docs/man/man1/kubectl-cluster-info.1 docs/man/man1/kubectl-config-current-context.1 docs/man/man1/kubectl-config-set-cluster.1 @@ -65,6 +66,7 @@ docs/user-guide/kubectl/kubectl_apply.md docs/user-guide/kubectl/kubectl_attach.md docs/user-guide/kubectl/kubectl_autoscale.md docs/user-guide/kubectl/kubectl_cluster-info.md +docs/user-guide/kubectl/kubectl_cluster-info_dump.md docs/user-guide/kubectl/kubectl_config.md docs/user-guide/kubectl/kubectl_config_current-context.md docs/user-guide/kubectl/kubectl_config_set-cluster.md diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 528ae4c6d6a..992bafa999e 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -3361,10 +3361,57 @@ _kubectl_config() noun_aliases=() } +_kubectl_cluster-info_dump() +{ + last_command="kubectl_cluster-info_dump" + commands=() + + flags=() + two_word_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--all-namespaces") + flags+=("--namespaces=") + flags+=("--output-directory=") + flags+=("--alsologtostderr") + flags+=("--api-version=") + flags+=("--as=") + flags+=("--certificate-authority=") + flags+=("--client-certificate=") + flags+=("--client-key=") + flags+=("--cluster=") + flags+=("--context=") + flags+=("--insecure-skip-tls-verify") + flags+=("--kubeconfig=") + flags+=("--log-backtrace-at=") + flags+=("--log-dir=") + flags+=("--log-flush-frequency=") + flags+=("--logtostderr") + flags+=("--match-server-version") + flags+=("--namespace=") + flags_with_completion+=("--namespace") + flags_completion+=("__kubectl_get_namespaces") + flags+=("--password=") + flags+=("--server=") + two_word_flags+=("-s") + flags+=("--stderrthreshold=") + flags+=("--token=") + flags+=("--user=") + flags+=("--username=") + flags+=("--v=") + flags+=("--vmodule=") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + _kubectl_cluster-info() { last_command="kubectl_cluster-info" commands=() + commands+=("dump") flags=() two_word_flags=() diff --git a/docs/man/man1/kubectl-cluster-info.1 b/docs/man/man1/kubectl-cluster-info.1 index fb63196cb08..459594a3366 100644 --- a/docs/man/man1/kubectl-cluster-info.1 +++ b/docs/man/man1/kubectl-cluster-info.1 @@ -122,7 +122,7 @@ Display addresses of the master and services with label kubernetes.io/cluster\-s .SH SEE ALSO .PP -\fBkubectl(1)\fP, +\fBkubectl(1)\fP, \fBkubectl\-cluster\-info\-dump(1)\fP, .SH HISTORY diff --git a/docs/user-guide/kubectl/kubectl_cluster-info.md b/docs/user-guide/kubectl/kubectl_cluster-info.md index 82caed80e7b..e65ab21a3bb 100644 --- a/docs/user-guide/kubectl/kubectl_cluster-info.md +++ b/docs/user-guide/kubectl/kubectl_cluster-info.md @@ -82,8 +82,9 @@ kubectl cluster-info ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager +* [kubectl cluster-info dump](kubectl_cluster-info_dump.md) - Dump lots of relevant info for debugging and diagnosis. -###### Auto generated by spf13/cobra on 5-Apr-2016 +###### Auto generated by spf13/cobra on 27-Apr-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_cluster-info.md?pixel)]() diff --git a/docs/yaml/kubectl/kubectl_cluster-info.yaml b/docs/yaml/kubectl/kubectl_cluster-info.yaml index 9aeee3f3de4..44ad329311a 100644 --- a/docs/yaml/kubectl/kubectl_cluster-info.yaml +++ b/docs/yaml/kubectl/kubectl_cluster-info.yaml @@ -70,3 +70,4 @@ inherited_options: comma-separated list of pattern=N settings for file-filtered logging see_also: - kubectl +- dump diff --git a/pkg/kubectl/cmd/clusterinfo.go b/pkg/kubectl/cmd/clusterinfo.go index 9fc3035865b..b5f90730ee0 100644 --- a/pkg/kubectl/cmd/clusterinfo.go +++ b/pkg/kubectl/cmd/clusterinfo.go @@ -43,6 +43,7 @@ func NewCmdClusterInfo(f *cmdutil.Factory, out io.Writer) *cobra.Command { }, } cmdutil.AddInclude3rdPartyFlags(cmd) + cmd.AddCommand(NewCmdClusterInfoDump(f, out)) return cmd } diff --git a/pkg/kubectl/cmd/clusterinfo_dump.go b/pkg/kubectl/cmd/clusterinfo_dump.go new file mode 100644 index 00000000000..36e543a61c9 --- /dev/null +++ b/pkg/kubectl/cmd/clusterinfo_dump.go @@ -0,0 +1,204 @@ +/* +Copyright 2016 The Kubernetes Authors 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 ( + "fmt" + "io" + "os" + "path" + + "github.com/spf13/cobra" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/kubectl" + cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" +) + +// NewCmdCreateSecret groups subcommands to create various types of secrets +func NewCmdClusterInfoDump(f *cmdutil.Factory, cmdOut io.Writer) *cobra.Command { + cmd := &cobra.Command{ + Use: "dump", + Short: "Dump lots of relevant info for debugging and diagnosis.", + Long: dumpLong, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(dumpClusterInfo(f, cmd, args, cmdOut)) + }, + } + cmd.Flags().String("output-directory", "", "Where to output the files. If empty or '-' uses stdout, otherwise creates a directory hierarchy in that directory") + cmd.Flags().StringSlice("namespaces", []string{}, "A comma separated list of namespaces to dump.") + cmd.Flags().Bool("all-namespaces", false, "If true, dump all namespaces. If true, --namespaces is ignored.") + return cmd +} + +const ( + dumpLong = ` +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. +` + + dumpExample = ` # Dump current cluster state to stdout + kubectl cluster-info dump + + # Dump current cluster state to /tmp + kubectl cluster-info dump --output-directory=/tmp + + # Dump all namespaces to stdout + kubectl cluster-info dump --all-namespaces + + # Dump a set of namespaces to /tmp + kubectl cluster-info dump --namespaces default,kube-system --output-directory=/tmp` +) + +func getWriter(cmd *cobra.Command, out io.Writer, filename string) io.Writer { + dir := cmdutil.GetFlagString(cmd, "output-directory") + if len(dir) == 0 || dir == "-" { + return out + } + 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 +} + +func dumpClusterInfo(f *cmdutil.Factory, cmd *cobra.Command, args []string, out io.Writer) error { + var c *unversioned.Client + var err error + if c, err = f.Client(); err != nil { + return err + } + printer, _, err := kubectl.GetPrinter("json", "") + if err != nil { + return err + } + + nodes, err := c.Nodes().List(api.ListOptions{}) + if err != nil { + return err + } + + if err := printer.PrintObj(nodes, getWriter(cmd, out, "nodes.json")); err != nil { + return err + } + + var namespaces []string + if cmdutil.GetFlagBool(cmd, "all-namespaces") { + namespaceList, err := c.Namespaces().List(api.ListOptions{}) + if err != nil { + return err + } + for ix := range namespaceList.Items { + namespaces = append(namespaces, namespaceList.Items[ix].Name) + } + } else { + namespaces = cmdutil.GetFlagStringSlice(cmd, "namespaces") + if len(namespaces) == 0 { + cmdNamespace, _, err := f.DefaultNamespace() + if err != nil { + return err + } + namespaces = []string{ + api.NamespaceSystem, + cmdNamespace, + } + } + } + for _, namespace := range namespaces { + // TODO: this is repetitive in the extreme. Use reflection or + // something to make this a for loop. + events, err := c.Events(namespace).List(api.ListOptions{}) + if err != nil { + return err + } + if err := printer.PrintObj(events, getWriter(cmd, out, path.Join(namespace, "events.json"))); err != nil { + return err + } + + rcs, err := c.ReplicationControllers(namespace).List(api.ListOptions{}) + if err != nil { + return err + } + if err := printer.PrintObj(rcs, getWriter(cmd, out, path.Join(namespace, "replication-controllers.json"))); err != nil { + return err + } + + svcs, err := c.Services(namespace).List(api.ListOptions{}) + if err != nil { + return err + } + if err := printer.PrintObj(svcs, getWriter(cmd, out, path.Join(namespace, "services.json"))); err != nil { + return err + } + + sets, err := c.DaemonSets(namespace).List(api.ListOptions{}) + if err != nil { + return err + } + if err := printer.PrintObj(sets, getWriter(cmd, out, path.Join(namespace, "daemonsets.json"))); err != nil { + return err + } + + deps, err := c.Deployments(namespace).List(api.ListOptions{}) + if err != nil { + return err + } + if err := printer.PrintObj(deps, getWriter(cmd, out, path.Join(namespace, "deployments.json"))); err != nil { + return err + } + + rps, err := c.ReplicaSets(namespace).List(api.ListOptions{}) + if err != nil { + return err + } + if err := printer.PrintObj(rps, getWriter(cmd, out, path.Join(namespace, "replicasets.json"))); err != nil { + return err + } + + pods, err := c.Pods(namespace).List(api.ListOptions{}) + if err != nil { + return err + } + + if err := printer.PrintObj(pods, getWriter(cmd, out, path.Join(namespace, "pods.json"))); err != nil { + return err + } + + for ix := range pods.Items { + pod := &pods.Items[ix] + writer := getWriter(cmd, out, path.Join(namespace, pod.Name, "logs.txt")) + writer.Write([]byte(fmt.Sprintf("==== START logs for %s/%s ====\n", pod.Namespace, pod.Name))) + request, err := f.LogsForObject(pod, &api.PodLogOptions{}) + if err != nil { + return err + } + + data, err := request.DoRaw() + if err != nil { + return err + } + writer.Write(data) + writer.Write([]byte(fmt.Sprintf("==== END logs for %s/%s ====\n", pod.Namespace, pod.Name))) + } + } + return nil +}