Skip to content

Commit

Permalink
improve tkctl get command output (#822)
Browse files Browse the repository at this point in the history
* format code

* fix wrong binary name

* support output info with -owide

* improve get command output

* remove unused code

* add more example
  • Loading branch information
onlymellb authored and shuijing198799 committed Aug 27, 2019
1 parent 3534fa5 commit c62324d
Show file tree
Hide file tree
Showing 10 changed files with 248 additions and 83 deletions.
2 changes: 1 addition & 1 deletion pkg/tkctl/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ package cmd

import (
"flag"
"github.com/pingcap/tidb-operator/pkg/tkctl/cmd/version"
"io"

"github.com/pingcap/tidb-operator/pkg/tkctl/cmd/completion"
Expand All @@ -26,6 +25,7 @@ import (
"github.com/pingcap/tidb-operator/pkg/tkctl/cmd/list"
"github.com/pingcap/tidb-operator/pkg/tkctl/cmd/upinfo"
"github.com/pingcap/tidb-operator/pkg/tkctl/cmd/use"
"github.com/pingcap/tidb-operator/pkg/tkctl/cmd/version"
"github.com/pingcap/tidb-operator/pkg/tkctl/config"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
Expand Down
7 changes: 4 additions & 3 deletions pkg/tkctl/cmd/ctop/ctop.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ package ctop

import (
"fmt"
"strings"

"github.com/pingcap/tidb-operator/pkg/tkctl/config"
"github.com/pingcap/tidb-operator/pkg/tkctl/executor"
"github.com/pingcap/tidb-operator/pkg/tkctl/util"
Expand All @@ -26,16 +28,15 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"strings"
)

const (
ctopExample = `
# ctop the specified pod
tkc ctop POD_NAME
tkctl ctop POD_NAME
# ctop the specified node
tkc ctop node/NODE_NAME
tkctl ctop node/NODE_NAME
`
ctopUsage = "expected 'ctop POD_NAME' or 'ctop node/NODE_NAME' for the ctop command"
defaultImage = "quay.io/vektorlab/ctop:0.7.2"
Expand Down
9 changes: 5 additions & 4 deletions pkg/tkctl/cmd/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package debug

import (
"fmt"

"github.com/pingcap/tidb-operator/pkg/tkctl/config"
"github.com/pingcap/tidb-operator/pkg/tkctl/executor"
"github.com/pingcap/tidb-operator/pkg/tkctl/util"
Expand All @@ -31,16 +32,16 @@ import (
const (
debugExample = `
# debug a container in the running pod, the first container will be picked by default
tkc debug POD_NAME
tkctl debug POD_NAME
# specify namespace or container
tkc debug --namespace foo POD_NAME -c CONTAINER_NAME
tkctl debug --namespace foo POD_NAME -c CONTAINER_NAME
# override the default troubleshooting image
tkc debug POD_NAME --image aylei/debug-jvm
tkctl debug POD_NAME --image aylei/debug-jvm
# override entrypoint of debug container
tkc debug POD_NAME --image aylei/debug-jvm /bin/bash
tkctl debug POD_NAME --image aylei/debug-jvm /bin/bash
`
debugLongDesc = `
Expand Down
247 changes: 198 additions & 49 deletions pkg/tkctl/cmd/get/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,53 @@
package get

import (
"encoding/json"
"fmt"
"strings"

"github.com/pingcap/tidb-operator/pkg/apis/pingcap.com/v1alpha1"
"github.com/pingcap/tidb-operator/pkg/client/clientset/versioned"
"github.com/pingcap/tidb-operator/pkg/label"
"github.com/pingcap/tidb-operator/pkg/tkctl/config"
"github.com/pingcap/tidb-operator/pkg/tkctl/readable"
"github.com/spf13/cobra"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/kubernetes"
apicore "k8s.io/kubernetes/pkg/apis/core"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
kubeprinters "k8s.io/kubernetes/pkg/printers"
"strings"
)

const (
getLongDesc = `
Get tidb component detail.
Available components include: all, pd, tidb, tikv, volume
You can omit --tidbcluster=<name> option by running 'tkc use <name>',
You can omit --tidbcluster=<name> option by running 'tkctl use <name>',
`
getExample = `
# get PD details
tkc get pd
tkctl get pd
# get multiple kinds of resources
tkc get tikv,tidb,volume
tkctl get tikv,tidb,volume
# get volume details and choose different format
tkc get volume -o yaml
tkctl get volume -o yaml
# get details of a specific pd/tikv/tidb/volume
tkctl get volume <volume-name> -oyaml
# output all columns, including omitted columns
tkctl get pd,volume -owide
# get all components
tkc get all
tkctl get all
`
getUsage = "expect 'get -t=CLUSTER_NAME kind | get -A kind' for get command or set tidb cluster by 'use' first"
)
Expand All @@ -67,12 +79,14 @@ type GetOptions struct {
AllClusters bool
Namespace string
TidbClusterName string
ResourceName string
GetPD bool
GetTiKV bool
GetTiDB bool
GetVolume bool

PrintFlags *readable.PrintFlags
IsHumanReadablePrinter bool
PrintFlags *readable.PrintFlags

tcCli *versioned.Clientset
kubeCli *kubernetes.Clientset
Expand All @@ -83,7 +97,6 @@ type GetOptions struct {
// NewCmdGet creates the get command which get the tidb component detail
func NewCmdGet(tkcContext *config.TkcContext, streams genericclioptions.IOStreams) *cobra.Command {
options := NewGetOptions(streams)

cmd := &cobra.Command{
Use: "get",
Short: "get pd|tikv|tidb|volume|all",
Expand Down Expand Up @@ -147,6 +160,11 @@ func (o *GetOptions) Complete(tkcContext *config.TkcContext, cmd *cobra.Command,
}
o.kubeCli = kubeClient

// human readable printers have special conversion rules, so we determine if we're using one.
if len(o.PrintFlags.OutputFormat) == 0 || o.PrintFlags.OutputFormat == "wide" {
o.IsHumanReadablePrinter = true
}

resources := args[0]
for _, resource := range strings.Split(resources, ",") {
switch resource {
Expand All @@ -166,6 +184,10 @@ func (o *GetOptions) Complete(tkcContext *config.TkcContext, cmd *cobra.Command,
o.GetVolume = true
}
}

if len(args) > 1 {
o.ResourceName = args[1]
}
return nil
}

Expand All @@ -190,50 +212,177 @@ func (o *GetOptions) Run(tkcContext *config.TkcContext, cmd *cobra.Command, args
tcs = []v1alpha1.TidbCluster{*tc}
}

printer, err := o.PrintFlags.ToPrinter(false, o.AllClusters)
if err != nil {
return err
}
w := kubeprinters.GetNewTabWriter(o.Out)
printTidbInfo := len(tcs) > 1
multiTidbCluster := len(tcs) > 1
var errs []error
for i := range tcs {
tc := tcs[i]
if printTidbInfo {
w.Write([]byte(fmt.Sprintf("Cluster: %s/%s\n", tc.Namespace, tc.Name)))
w.Flush()
}
flushPods := func(kind string) {
podList, err := o.kubeCli.CoreV1().Pods(tc.Namespace).List(metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s,%s=%s", label.InstanceLabelKey, tc.Name, label.ComponentLabelKey, kind),
})
if err != nil {
errs = append(errs, err)
}
printer.PrintObj(podList, w)
w.Flush()
for i, tc := range tcs {
if multiTidbCluster {
o.Out.Write([]byte(fmt.Sprintf("Cluster: %s/%s\n", tc.Namespace, tc.Name)))
}

// TODO: do a big batch or steadily print parts in minor step?
if o.GetPD {
flushPods(kindPD)
}
if o.GetTiKV {
flushPods(kindTiKV)
}
if o.GetTiDB {
flushPods(kindTiDB)
}
if o.GetVolume {
volumeList, err := o.kubeCli.CoreV1().PersistentVolumes().List(metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s,%s=%s", label.InstanceLabelKey, tc.Name,
label.NamespaceLabelKey, tc.Namespace),
})
if err != nil {
return err
if err := o.PrintOutput(&tc, kindPD, o.GetPD); err != nil {
errs = append(errs, err)
}

if err := o.PrintOutput(&tc, kindTiKV, o.GetTiKV); err != nil {
errs = append(errs, err)
}

if err := o.PrintOutput(&tc, kindTiDB, o.GetTiDB); err != nil {
errs = append(errs, err)
}

if err := o.PrintOutput(&tc, kindVolume, o.GetVolume); err != nil {
errs = append(errs, err)
}

if multiTidbCluster && i != len(tcs)-1 {
o.Out.Write([]byte("\n"))
}
}
return utilerrors.NewAggregate(errs)
}

func (o *GetOptions) PrintOutput(tc *v1alpha1.TidbCluster, resourceType string, needPrint bool) error {
if !needPrint {
return nil
}

switch resourceType {
case kindPD, kindTiDB, kindTiKV:
var objs []runtime.Object
var listOptions metav1.ListOptions
if len(o.ResourceName) == 0 {
listOptions = metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s,%s=%s", label.InstanceLabelKey, tc.Name, label.ComponentLabelKey, resourceType),
}
printer.PrintObj(volumeList, w)
w.Flush()
} else {
listOptions = metav1.ListOptions{
FieldSelector: fmt.Sprintf("metadata.name=%s", o.ResourceName),
}
}

podList, err := o.kubeCli.CoreV1().Pods(tc.Namespace).List(listOptions)
if err != nil {
return err
}
for _, pod := range podList.Items {
pod.GetObjectKind().SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("Pod"))
objs = append(objs, &pod)
}

if !o.IsHumanReadablePrinter {
return o.printGeneric(objs)
}

printer, err := o.PrintFlags.ToPrinter(false, o.AllClusters)
if err != nil {
return err
}

return printer.PrintObj(podList, o.Out)
case kindVolume:
var objs []runtime.Object
var listOptions metav1.ListOptions
if len(o.ResourceName) == 0 {
listOptions = metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s,%s=%s", label.InstanceLabelKey, tc.Name, label.NamespaceLabelKey, tc.Namespace),
}
} else {
listOptions = metav1.ListOptions{
FieldSelector: fmt.Sprintf("metadata.name=%s", o.ResourceName),
}
}

volumeList, err := o.kubeCli.CoreV1().PersistentVolumes().List(listOptions)
if err != nil {
return err
}
for _, volume := range volumeList.Items {
volume.GetObjectKind().SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("PersistentVolume"))
objs = append(objs, &volume)
}

if !o.IsHumanReadablePrinter {
return o.printGeneric(objs)
}

// PersistentVolume without namespace concept
printer, err := o.PrintFlags.ToPrinter(false, false)
if err != nil {
return err
}

return printer.PrintObj(volumeList, o.Out)
}
return nil
return fmt.Errorf("Unknow resource type %s", resourceType)
}

func (o *GetOptions) printGeneric(objs []runtime.Object) error {
printer, err := o.PrintFlags.ToPrinter(false, false)
if err != nil {
return err
}

if len(objs) == 0 {
return nil
}

var allObj runtime.Object
if len(objs) > 1 {
list := apicore.List{
TypeMeta: metav1.TypeMeta{
Kind: "List",
APIVersion: "v1",
},
ListMeta: metav1.ListMeta{},
}
for _, obj := range objs {
list.Items = append(list.Items, obj)
}

listData, err := json.Marshal(list)
if err != nil {
return err
}

converted, err := runtime.Decode(unstructured.UnstructuredJSONScheme, listData)
if err != nil {
return err
}

allObj = converted
} else {
allObj = objs[0]
}

isList := meta.IsListType(allObj)
if !isList {
return printer.PrintObj(allObj, o.Out)
}

items, err := meta.ExtractList(allObj)
if err != nil {
return err
}

// take the items and create a new list for display
list := &unstructured.UnstructuredList{
Object: map[string]interface{}{
"kind": "List",
"apiVersion": "v1",
"metadata": map[string]interface{}{},
},
}
if listMeta, err := meta.ListAccessor(allObj); err == nil {
list.Object["metadata"] = map[string]interface{}{
"selfLink": listMeta.GetSelfLink(),
"resourceVersion": listMeta.GetResourceVersion(),
}
}

for _, item := range items {
list.Items = append(list.Items, *item.(*unstructured.Unstructured))
}
return printer.PrintObj(list, o.Out)
}
Loading

0 comments on commit c62324d

Please sign in to comment.