Skip to content
This repository has been archived by the owner on Jul 25, 2022. It is now read-only.

Commit

Permalink
gardenctl diag command to display diagnostic information of a shoot
Browse files Browse the repository at this point in the history
  • Loading branch information
neo-liang-sap committed Nov 3, 2020
1 parent 2c75ef6 commit 044d93a
Show file tree
Hide file tree
Showing 59 changed files with 9,297 additions and 0 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/ghodss/yaml v1.0.0
github.com/golang/mock v1.4.3
github.com/jmoiron/jsonq v0.0.0-20150511023944-e874b168d07e
github.com/olekukonko/tablewriter v0.0.4
github.com/onsi/ginkgo v1.10.1
github.com/onsi/gomega v1.7.0
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
Expand All @@ -20,6 +21,7 @@ require (
k8s.io/api v0.17.0
k8s.io/apimachinery v0.17.0
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
k8s.io/metrics v0.16.8
)

replace (
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU=
Expand All @@ -425,6 +426,7 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+
github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.4.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
Expand Down Expand Up @@ -914,6 +916,7 @@ k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKf
k8s.io/kubelet v0.0.0-20190918162654-250a1838aa2c/go.mod h1:LGhpyzd/3AkWcFcQJ3yO1UxMnJ6urMkCYfCp4iVxhjs=
k8s.io/kubelet v0.16.8/go.mod h1:mzDpnryQg2dlB6V3/WAgb1baIamiICtWpXMFrPOFh6I=
k8s.io/metrics v0.0.0-20191004105854-2e8cf7d0888c/go.mod h1:a25VAbm3QT3xiVl1jtoF1ueAKQM149UdZ+L93ePfV3M=
k8s.io/metrics v0.16.8 h1:VHYjoncB4WjvizvQ36vQ2kga4jo7+hLhJIYi60JGru0=
k8s.io/metrics v0.16.8/go.mod h1:uBIJKJKdga8vL76a1dl+eRlUqOAdCbBpvFHC28SbUIY=
k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5 h1:VBM/0P5TWxwk+Nw6Z+lAw3DKgO76g90ETOiA6rfLV1Y=
k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
Expand Down
295 changes: 295 additions & 0 deletions pkg/cmd/diag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
// Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
//
// 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 (
"errors"
"fmt"
"io/ioutil"
"os"
"strconv"
"time"

"github.com/gardener/gardener/pkg/apis/core/v1beta1"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
metricsv "k8s.io/metrics/pkg/client/clientset/versioned"
)

// NewDiagCmd returns diagnostic information for a shoot.
func NewDiagCmd(reader TargetReader, ioStreams IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "diag",
Short: "Print shoot diagnostic information",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
target := reader.ReadTarget(pathTarget)
if !CheckShootIsTargeted(target) {
return errors.New("no shoot targeted")
}

shoot, err := FetchShootFromTarget(target)
checkError(err)
getShootInformation(shoot, target)
return nil
},
}
return cmd
}

//getShootInformation prints all information regarding a shoot
func getShootInformation(shoot *v1beta1.Shoot, target TargetInterface) {
fmt.Println("The shoot diagnostic information are as follows:")
fmt.Println()
fmt.Println("Shoot: " + shoot.Name)
fmt.Println("Kubernetes Version: " + shoot.Spec.Kubernetes.Version)
fmt.Println("Created At: " + shoot.ObjectMeta.CreationTimestamp.String())
annotationsMap := shoot.GetObjectMeta().GetAnnotations()
fmt.Println("Created By: " + annotationsMap["gardener.cloud/created-by"])
fmt.Println("Cloud Profile: " + shoot.Spec.CloudProfileName)
fmt.Println("Region: " + shoot.Spec.Region)
fmt.Println("Purpose: " + *shoot.Spec.Purpose)
fmt.Println("Seed Name: " + *shoot.Status.SeedName)
fmt.Println()

fmt.Println("Last Operation:")
fmt.Println()
lastOperationMsg := []string{shoot.Status.LastOperation.Description, fmt.Sprintf("%v", shoot.Status.LastOperation.Type), fmt.Sprintf("%v", shoot.Status.LastOperation.State)}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Description", "Last Operation Type", "Last Operation Type State"})
table.Append(lastOperationMsg)
table.Render()
fmt.Println()

fmt.Println("Shoot Conditions: ")
fmt.Println()
data := [][]string{}
for i := range shoot.Status.Conditions {
condition := shoot.Status.Conditions[i]
codesValue := condition.Codes
codesString := fmt.Sprintf("%v", codesValue)
data = append(data, []string{condition.Message, condition.LastTransitionTime.String(), codesString})
}
table = tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Message", "Last Transition Time", "Codes"})
for _, v := range data {
table.Append(v)
}
table.Render()
fmt.Println()
fmt.Println("Workers Groups:")
fmt.Println()
data = [][]string{}
table = tablewriter.NewWriter(os.Stdout)
for w := range shoot.Spec.Provider.Workers {
worker := shoot.Spec.Provider.Workers[w]
if worker.Volume != nil {
data = append(data, []string{worker.Name, strconv.Itoa(int(worker.Minimum)), strconv.Itoa(int(worker.Maximum)), fmt.Sprintf("%v", worker.MaxUnavailable), fmt.Sprintf("%v", worker.MaxSurge), worker.Machine.Image.Name, *worker.Machine.Image.Version, worker.Machine.Type, fmt.Sprintf("%v", worker.Zones), fmt.Sprintf("%v", worker.Volume.Name), fmt.Sprintf("%v", worker.Volume.Type), worker.Volume.VolumeSize})
table.SetHeader([]string{"Worker Name", "Min", "Max", "Max Unavailable", "Max Surge", "Image Name", "Image Version", "Image Type", "Zones", "Volume Name", "Volume Type", "Volume Size"})
} else {
data = append(data, []string{worker.Name, strconv.Itoa(int(worker.Minimum)), strconv.Itoa(int(worker.Maximum)), fmt.Sprintf("%v", worker.MaxUnavailable), fmt.Sprintf("%v", worker.MaxSurge), worker.Machine.Image.Name, *worker.Machine.Image.Version, worker.Machine.Type, fmt.Sprintf("%v", worker.Zones)})
table.SetHeader([]string{"Worker Name", "Min", "Max", "Max Unavailable", "Max Surge", "Image Name", "Image Version", "Image Type", "Zones"})
}
}
for _, v := range data {
table.Append(v)
}
table.Render()

if !shoot.Status.IsHibernated {
var err error
var shootClient kubernetes.Interface
if shootClient, err = target.K8SClientToKind(TargetKindShoot); err != nil {
checkError(err)
}
var nodes *corev1.NodeList
if nodes, err = shootClient.CoreV1().Nodes().List(metav1.ListOptions{}); err != nil {
checkError(err)
}

kubeconfig, err := ioutil.ReadFile(*kubeconfig)
checkError(err)
clientConfig, err := clientcmd.NewClientConfigFromBytes(kubeconfig)
checkError(err)
config, err := clientConfig.ClientConfig()
checkError(err)
metricsClientset, err := metricsv.NewForConfig(config)
checkError(err)
nodeMetricsList, err := metricsClientset.MetricsV1beta1().NodeMetricses().List(metav1.ListOptions{})
checkError(err)
fmt.Println()
fmt.Println("Node Metrics:")
fmt.Println()
data = [][]string{}
for _, metric := range nodeMetricsList.Items {
cpuUsage, _ := metric.Usage.Cpu().AsInt64()
memUsage, _ := metric.Usage.Memory().AsInt64()

data = append(data, []string{metric.GetName(), fmt.Sprintf("%v", cpuUsage), fmt.Sprintf("%v", memUsage/1000)})
}
table = tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Node Name", "CPU Usage (Core)", "Memory Usage (MB)"})

for _, v := range data {
table.Append(v)
}
table.Render()
fmt.Println()
fmt.Println("System Components:")
fmt.Println()
systemPods, err := shootClient.CoreV1().Pods("kube-system").List(metav1.ListOptions{})
checkError(err)
data := [][]string{}
for _, pod := range systemPods.Items {
readyNumber := 0
totalNumber := 0
for pss := range pod.Status.ContainerStatuses {
if pod.Status.ContainerStatuses[pss].Ready {
readyNumber++
}
if pod.Status.ContainerStatuses[pss].State.Terminated == nil {
totalNumber++
}
}
podstatusPhase := string(pod.Status.Phase)
podCreationTime := pod.GetCreationTimestamp()
age := time.Since(podCreationTime.Time).Round(time.Second)
timeString := fmt.Sprintf("%v", podCreationTime)

data = append(data, []string{pod.GetName(), podstatusPhase, strconv.Itoa(readyNumber), strconv.Itoa(totalNumber), timeString, age.String()})
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Phase", "Ready", "Total", "Created", "Age"})
for _, v := range data {
table.Append(v)
}
table.Render()

daemonSets, err := shootClient.AppsV1().DaemonSets("kube-system").List(metav1.ListOptions{})
checkError(err)
fmt.Println()
fmt.Println("DaemonSets:")
fmt.Println()
data = [][]string{}
for _, ds := range daemonSets.Items {
data = append(data, []string{ds.GetName(), strconv.Itoa(int(ds.Status.DesiredNumberScheduled)), strconv.Itoa(int(ds.Status.NumberAvailable))})
}
table = tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Desired Number", "Current Number"})
for _, v := range data {
table.Append(v)
}
table.Render()

fmt.Println()
fmt.Println("Nodes:")
fmt.Println()
data = [][]string{}
for _, n := range nodes.Items {
addString := ""
for a := range n.Status.Addresses {
if n.Status.Addresses[a].Type == "InternalIP" {
addString = n.Status.Addresses[a].Address
}
}
cpuCount, _ := n.Status.Capacity.Cpu().AsInt64()
memoryCount, _ := n.Status.Capacity.Memory().AsInt64()
data = append(data, []string{n.Name, n.Spec.ProviderID, addString, strconv.Itoa(int(cpuCount)), strconv.Itoa(int(memoryCount / 1000))})
}
table = tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Node Name", "Provider ID", "Address", "CPU Cores", "Memory (MB)"})
for _, v := range data {
table.Append(v)
}
table.Render()
fmt.Println()
fmt.Println("BlockingDisruptionBudgets:")
fmt.Println()
dbds, err := shootClient.PolicyV1beta1().PodDisruptionBudgets("kube-system").List(metav1.ListOptions{})
checkError(err)
data = [][]string{}
for dbdsIndex := range dbds.Items {
pdb := dbds.Items[dbdsIndex]
data = append(data, []string{pdb.Spec.Selector.MatchLabels["k8s-app"], fmt.Sprintf("%v", pdb.Spec.MinAvailable), fmt.Sprintf("%v", pdb.Spec.MaxUnavailable)})
}
table = tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Min Available", "Max Unavailable"})
for _, v := range data {
table.Append(v)
}
table.Render()
fmt.Println()
fmt.Println("MutatingWebhookConfigurations:")
fmt.Println()
data = [][]string{}
mutatingWebhookConfigurations, err := shootClient.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().List(metav1.ListOptions{})
checkError(err)
for mwc := range mutatingWebhookConfigurations.Items {
whList := mutatingWebhookConfigurations.Items[mwc].Webhooks
for whIndex := range whList {
wh := whList[whIndex]
data = append(data, []string{wh.Name})
}
}
table = tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"MutatingWebhookConfiguration Name"})

for _, v := range data {
table.Append(v)
}
table.Render()
seedClient, err := target.K8SClientToKind(TargetKindSeed)
checkError(err)
controlPlanePods, err := seedClient.CoreV1().Pods(shoot.Status.TechnicalID).List(metav1.ListOptions{})
checkError(err)
fmt.Println()
fmt.Println("Control Plane Pods:")
fmt.Println()
data = [][]string{}
for _, pod := range controlPlanePods.Items {
readyNumber := 0
totalNumber := 0
for pss := range pod.Status.ContainerStatuses {
if pod.Status.ContainerStatuses[pss].Ready {
readyNumber++
}
if pod.Status.ContainerStatuses[pss].State.Terminated == nil {
totalNumber++
}
}
podstatusPhase := string(pod.Status.Phase)
podCreationTime := pod.GetCreationTimestamp()
age := time.Since(podCreationTime.Time).Round(time.Second)
data = append(data, []string{pod.GetName(), podstatusPhase, strconv.Itoa(readyNumber), strconv.Itoa(totalNumber), fmt.Sprintf("%v", podCreationTime), age.String()})
}
table = tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Phase", "Ready Number", "Total Number", "Creation Time", "Age"})

for _, v := range data {
table.Append(v)
}
table.Render()

} else {
fmt.Println()
fmt.Println("This shoot is now in hibernating status")
fmt.Println("Information like Nodes/Metrics/PDBs/Web hooks/etc will not be displayed")
}

}
1 change: 1 addition & 0 deletions pkg/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ func init() {
RootCmd.AddCommand(NewAliyunCmd(targetReader), NewAwsCmd(targetReader), NewAzCmd(targetReader), NewGcloudCmd(targetReader), NewOpenstackCmd(targetReader))
RootCmd.AddCommand(NewInfoCmd(targetReader, ioStreams))
RootCmd.AddCommand(NewVersionCmd(), NewUpdateCheckCmd())
RootCmd.AddCommand(NewDiagCmd(targetReader, ioStreams))

RootCmd.SuggestionsMinimumDistance = suggestionsMinimumDistance
RootCmd.BashCompletionFunction = bashCompletionFunc
Expand Down
8 changes: 8 additions & 0 deletions vendor/github.com/mattn/go-runewidth/.travis.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions vendor/github.com/mattn/go-runewidth/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 044d93a

Please sign in to comment.