Feat: System Info & Diagnose (#4657)

* Feat: System Info & Diagnose

Signed-off-by: foursevenlove <foursevenlove@gmail.com>

* Fix:1.misspelling 2.license

Signed-off-by: foursevenlove <foursevenlove@gmail.com>

* Fix: pattern of imported package

Signed-off-by: foursevenlove <foursevenlove@gmail.com>

* Fix: pattern of imported package

Signed-off-by: foursevenlove <foursevenlove@gmail.com>

* Fix:1.return error instead of panic 2.get deployment by label instead of by namespace 3.when getting a single deployment, the result is displayed in multi rows. Feat: 1.the system info command displays the cpu and memory metrics 2.the system info command displays the numbers of ready pods and desired pods.

* Feat: 1.the system info command displays the environment variables

* Fix: Making syntax simple

* Feat(system info):1.ARGS多行展示2.指定名称时无须指定namespace3.优化界面展示,支持原始打印或者以wide/yaml形式打印4.指定名称时,打印更可读信息

Signed-off-by: foursevenlove <foursevenlove@gmail.com>

* Feat:add comment

Signed-off-by: foursevenlove <foursevenlove@gmail.com>

* Fix:syntactic redundancy

Signed-off-by: foursevenlove <foursevenlove@gmail.com>

* Feat:Display all ARGS

Signed-off-by: foursevenlove <foursevenlove@gmail.com>

* Feat:Display all ARGS

Signed-off-by: foursevenlove <foursevenlove@gmail.com>

Add signoff

* Fix:1.return error instead of panic 2.get deployment by label instead of by namespace 3.when getting a single deployment, the result is displayed in multi rows. Feat: 1.the system info command displays the cpu and memory metrics 2.the system info command displays the numbers of ready pods and desired pods.

Signed-off-by: foursevenlove <foursevenlove@gmail.com>

Signed-off-by: foursevenlove <foursevenlove@gmail.com>
This commit is contained in:
47
2022-08-26 16:51:17 +08:00
committed by GitHub
parent 20bd1eeec5
commit a4ad1ad40f
+163 -55
View File
@@ -17,10 +17,12 @@ limitations under the License.
package cli
import (
"bytes"
"context"
"fmt"
"strings"
"github.com/gosuri/uitable"
"github.com/oam-dev/cluster-gateway/pkg/generated/clientset/versioned"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -40,8 +42,8 @@ import (
const (
// FlagSpecify specifies the deployment name
FlagSpecify = "specify"
// FlagDeploymentNamespace specifies the namespace of deployment
FlagDeploymentNamespace = "namespace"
// FlagOutputFormat specifies the output format. One of: (wide | yaml)
FlagOutputFormat = "output"
)
// NewSystemCommand print system detail info
@@ -79,10 +81,21 @@ func NewSystemInfoCommand(c common.Args) *cobra.Command {
if err != nil {
return errors.Wrapf(err, "failed to get deployment name flag")
}
namespace, err := cmd.Flags().GetString(FlagDeploymentNamespace)
// Get output format from flag
outputFormat, err := cmd.Flags().GetString(FlagOutputFormat)
if err != nil {
return errors.Wrapf(err, "failed to get deployment namespace flag")
return errors.Wrapf(err, "failed to get output format flag")
}
if outputFormat != "" {
outputFormatOptions := map[string]struct{}{
"wide": {},
"yaml": {},
}
if _, exist := outputFormatOptions[outputFormat]; !exist {
return errors.Errorf("Outputformat must in wide | yaml !")
}
}
// Get kube config
config, err := c.GetConfig()
if err != nil {
return err
@@ -92,67 +105,58 @@ func NewSystemInfoCommand(c common.Args) *cobra.Command {
if err != nil {
return err
}
mc, err := metrics.NewForConfig(config)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Get deploymentsClient in all namespace
deployments, err := clientset.AppsV1().Deployments(metav1.NamespaceAll).List(
ctx,
metav1.ListOptions{
LabelSelector: "app.kubernetes.io/name=vela-core",
},
)
if err != nil {
return err
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if deployName != "" {
// DeployName is not empty, print the specified deployment's information in yaml type
if namespace == "" {
return errors.Errorf("An empty namespace can not be set when a resource name is provided. Use -n to specify the namespace.")
// DeployName is not empty, print the specified deployment's information
found := false
for _, deployment := range deployments.Items {
if deployment.Name == deployName {
table := SpecifiedFormatPrinter(deployment)
cmd.Println(table.String())
found = true
break
}
}
// Get specified deployment in specified namespace
deployment, err := clientset.AppsV1().Deployments(namespace).Get(
ctx,
deployName,
metav1.GetOptions{},
)
if err != nil {
return err
if !found {
return errors.Errorf("deployment \"%s\" not found", deployName)
}
// Set ManagedFields to nil because it's too long to read
deployment.ManagedFields = nil
deploymentYaml, _ := yaml.Marshal(deployment)
if err != nil {
return err
}
cmd.Println(string(deploymentYaml))
} else {
// DeployName is empty, print all deployment's partial args
// Get deploymentsClient in all namespace
deployments, err := clientset.AppsV1().Deployments(metav1.NamespaceAll).List(
ctx,
metav1.ListOptions{
LabelSelector: "app.kubernetes.io/name=vela-core",
},
)
// Get metrics clientset
mc, err := metrics.NewForConfig(config)
if err != nil {
return err
}
podMetricsList, err := mc.MetricsV1beta1().PodMetricses(metav1.NamespaceAll).List(
ctx,
metav1.ListOptions{},
)
if err != nil {
return err
switch outputFormat {
case "":
table, err := NormalFormatPrinter(ctx, deployments, mc)
if err != nil {
return err
}
cmd.Println(table.String())
case "wide":
table, err := WideFormatPrinter(ctx, deployments, mc)
if err != nil {
return err
}
cmd.Println(table.String())
case "yaml":
str, err := YamlFormatPrinter(deployments)
if err != nil {
return err
}
cmd.Println(str)
}
table := newUITable().AddRow("NAME", "NAMESPACE", "READY PODS", "IMAGE", "CPU(cores)", "MEMORY(bytes)", "ARGS", "ENVS")
cpuMetricMap, memMetricMap := ComputeMetricByDeploymentName(deployments, podMetricsList)
for _, deploy := range deployments.Items {
table.AddRow(
deploy.Name,
deploy.Namespace,
fmt.Sprintf("%d/%d", deploy.Status.ReadyReplicas, deploy.Status.Replicas),
deploy.Spec.Template.Spec.Containers[0].Image,
fmt.Sprintf("%dm", cpuMetricMap[deploy.Name]),
fmt.Sprintf("%dMi", memMetricMap[deploy.Name]),
limitStringLength(strings.Join(deploy.Spec.Template.Spec.Containers[0].Args, " "), 50),
limitStringLength(GetEnvVariable(deploy.Spec.Template.Spec.Containers[0].Env), 50),
)
}
cmd.Println(table.String())
}
return nil
},
@@ -161,10 +165,114 @@ func NewSystemInfoCommand(c common.Args) *cobra.Command {
},
}
cmd.Flags().StringP(FlagSpecify, "s", "", "Specify the name of the deployment to check detail information. If empty, it will print all deployments information. Default to be empty.")
cmd.Flags().StringP(FlagDeploymentNamespace, "n", "", "Specify the namespace of the deployment to check detail information. If empty, it will prints deployments information of all namespaces. Default to be empty. An empty namespace may not be can when a resource name is provided. ")
cmd.Flags().StringP(FlagOutputFormat, "o", "", "Specifies the output format. One of: (wide | yaml)")
return cmd
}
// SpecifiedFormatPrinter prints the specified deployment's information
func SpecifiedFormatPrinter(deployment v1.Deployment) *uitable.Table {
table := newUITable().
AddRow("Name:", deployment.Name).
AddRow("Namespace:", deployment.Namespace).
AddRow("CreationTimestamp:", deployment.CreationTimestamp).
AddRow("Labels:", Map2Str(deployment.Labels)).
AddRow("Annotations:", Map2Str(deployment.Annotations)).
AddRow("Selector:", Map2Str(deployment.Spec.Selector.MatchLabels)).
AddRow("Image:", deployment.Spec.Template.Spec.Containers[0].Image).
AddRow("Args:", strings.Join(deployment.Spec.Template.Spec.Containers[0].Args, "\n")).
AddRow("Envs:", GetEnvVariable(deployment.Spec.Template.Spec.Containers[0].Env)).
AddRow("Limits:", CPUMem(deployment.Spec.Template.Spec.Containers[0].Resources.Limits)).
AddRow("Requests:", CPUMem(deployment.Spec.Template.Spec.Containers[0].Resources.Requests))
table.MaxColWidth = 120
return table
}
// CPUMem returns the upsage of cpu and memory
func CPUMem(resourceList corev1.ResourceList) string {
b := new(bytes.Buffer)
fmt.Fprintf(b, "cpu=%s\n", resourceList.Cpu())
fmt.Fprintf(b, "memory=%s", resourceList.Memory())
return b.String()
}
// Map2Str converts map to string
func Map2Str(m map[string]string) string {
b := new(bytes.Buffer)
for key, value := range m {
fmt.Fprintf(b, "%s=%s\n", key, value)
}
if len(b.String()) > 1 {
return b.String()[:len(b.String())-1]
}
return b.String()
}
// NormalFormatPrinter prints information in format of normal
func NormalFormatPrinter(ctx context.Context, deployments *v1.DeploymentList, mc *metrics.Clientset) (*uitable.Table, error) {
podMetricsList, err := mc.MetricsV1beta1().PodMetricses(metav1.NamespaceAll).List(
ctx,
metav1.ListOptions{},
)
if err != nil {
return nil, err
}
table := newUITable().AddRow("NAME", "NAMESPACE", "READY PODS", "IMAGE", "CPU(cores)", "MEMORY(bytes)")
cpuMetricMap, memMetricMap := ComputeMetricByDeploymentName(deployments, podMetricsList)
for _, deploy := range deployments.Items {
table.AddRow(
deploy.Name,
deploy.Namespace,
fmt.Sprintf("%d/%d", deploy.Status.ReadyReplicas, deploy.Status.Replicas),
deploy.Spec.Template.Spec.Containers[0].Image,
fmt.Sprintf("%dm", cpuMetricMap[deploy.Name]),
fmt.Sprintf("%dMi", memMetricMap[deploy.Name]),
)
}
return table, nil
}
// WideFormatPrinter prints information in format of wide
func WideFormatPrinter(ctx context.Context, deployments *v1.DeploymentList, mc *metrics.Clientset) (*uitable.Table, error) {
podMetricsList, err := mc.MetricsV1beta1().PodMetricses(metav1.NamespaceAll).List(
ctx,
metav1.ListOptions{},
)
if err != nil {
return nil, err
}
table := newUITable().AddRow("NAME", "NAMESPACE", "READY PODS", "IMAGE", "CPU(cores)", "MEMORY(bytes)", "ARGS", "ENVS")
table.MaxColWidth = 100
cpuMetricMap, memMetricMap := ComputeMetricByDeploymentName(deployments, podMetricsList)
for _, deploy := range deployments.Items {
table.AddRow(
deploy.Name,
deploy.Namespace,
fmt.Sprintf("%d/%d", deploy.Status.ReadyReplicas, deploy.Status.Replicas),
deploy.Spec.Template.Spec.Containers[0].Image,
fmt.Sprintf("%dm", cpuMetricMap[deploy.Name]),
fmt.Sprintf("%dMi", memMetricMap[deploy.Name]),
strings.Join(deploy.Spec.Template.Spec.Containers[0].Args, " "),
limitStringLength(GetEnvVariable(deploy.Spec.Template.Spec.Containers[0].Env), 180),
)
}
return table, nil
}
// YamlFormatPrinter prints information in format of yaml
func YamlFormatPrinter(deployments *v1.DeploymentList) (string, error) {
str := ""
for _, deployment := range deployments.Items {
// Set ManagedFields to nil because it's too long to read
deployment.ManagedFields = nil
deploymentYaml, err := yaml.Marshal(deployment)
if err != nil {
return "", err
}
str += string(deploymentYaml)
}
return str, nil
}
// ComputeMetricByDeploymentName computes cpu and memory metric of deployment
func ComputeMetricByDeploymentName(deployments *v1.DeploymentList, podMetricsList *v1beta1.PodMetricsList) (cpuMetricMap, memMetricMap map[string]int64) {
cpuMetricMap = make(map[string]int64)