Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve tkctl get command output #822

Merged
merged 8 commits into from
Aug 27, 2019
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: 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