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

gardenctl diag command #413

Merged
merged 1 commit into from
Nov 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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