Fix karmadactl get -o yaml|json
Signed-off-by: lonelyCZ <531187475@qq.com>
This commit is contained in:
parent
74838ccce0
commit
f488d6be90
|
@ -2,6 +2,7 @@ package karmadactl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
@ -10,6 +11,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
@ -95,8 +97,9 @@ type CommandGetOptions struct {
|
||||||
|
|
||||||
Clusters []string
|
Clusters []string
|
||||||
|
|
||||||
PrintFlags *get.PrintFlags
|
PrintFlags *get.PrintFlags
|
||||||
ToPrinter func(*meta.RESTMapping, *bool, bool, bool) (printers.ResourcePrinterFunc, error)
|
ToPrinter func(*meta.RESTMapping, *bool, bool, bool) (printers.ResourcePrinterFunc, error)
|
||||||
|
IsHumanReadablePrinter bool
|
||||||
|
|
||||||
CmdParent string
|
CmdParent string
|
||||||
|
|
||||||
|
@ -113,6 +116,8 @@ type CommandGetOptions struct {
|
||||||
Namespace string
|
Namespace string
|
||||||
ExplicitNamespace bool
|
ExplicitNamespace bool
|
||||||
|
|
||||||
|
ServerPrint bool
|
||||||
|
|
||||||
NoHeaders bool
|
NoHeaders bool
|
||||||
Sort bool
|
Sort bool
|
||||||
IgnoreNotFound bool
|
IgnoreNotFound bool
|
||||||
|
@ -124,10 +129,11 @@ type CommandGetOptions struct {
|
||||||
// NewCommandGetOptions returns a GetOptions with default chunk size 500.
|
// NewCommandGetOptions returns a GetOptions with default chunk size 500.
|
||||||
func NewCommandGetOptions(parent string, streams genericclioptions.IOStreams) *CommandGetOptions {
|
func NewCommandGetOptions(parent string, streams genericclioptions.IOStreams) *CommandGetOptions {
|
||||||
return &CommandGetOptions{
|
return &CommandGetOptions{
|
||||||
PrintFlags: get.NewGetPrintFlags(),
|
PrintFlags: get.NewGetPrintFlags(),
|
||||||
CmdParent: parent,
|
CmdParent: parent,
|
||||||
IOStreams: streams,
|
IOStreams: streams,
|
||||||
ChunkSize: 500,
|
ChunkSize: 500,
|
||||||
|
ServerPrint: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,8 +141,19 @@ func NewCommandGetOptions(parent string, streams genericclioptions.IOStreams) *C
|
||||||
func (g *CommandGetOptions) Complete(cmd *cobra.Command, args []string) error {
|
func (g *CommandGetOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||||
newScheme := gclient.NewSchema()
|
newScheme := gclient.NewSchema()
|
||||||
|
|
||||||
if g.AllNamespaces {
|
outputOption := cmd.Flags().Lookup("output").Value.String()
|
||||||
g.ExplicitNamespace = false
|
if strings.Contains(outputOption, "custom-columns") || outputOption == "yaml" || strings.Contains(outputOption, "json") {
|
||||||
|
g.ServerPrint = false
|
||||||
|
}
|
||||||
|
|
||||||
|
templateArg := ""
|
||||||
|
if g.PrintFlags.TemplateFlags != nil && g.PrintFlags.TemplateFlags.TemplateArgument != nil {
|
||||||
|
templateArg = *g.PrintFlags.TemplateFlags.TemplateArgument
|
||||||
|
}
|
||||||
|
|
||||||
|
// human readable printers have special conversion rules, so we determine if we're using one.
|
||||||
|
if (len(*g.PrintFlags.OutputFormat) == 0 && len(templateArg) == 0) || *g.PrintFlags.OutputFormat == "wide" {
|
||||||
|
g.IsHumanReadablePrinter = true
|
||||||
}
|
}
|
||||||
|
|
||||||
g.ToPrinter = func(mapping *meta.RESTMapping, outputObjects *bool, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) {
|
g.ToPrinter = func(mapping *meta.RESTMapping, outputObjects *bool, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) {
|
||||||
|
@ -163,7 +180,9 @@ func (g *CommandGetOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
printer = &get.TablePrinter{Delegate: printer}
|
if g.ServerPrint {
|
||||||
|
printer = &get.TablePrinter{Delegate: printer}
|
||||||
|
}
|
||||||
|
|
||||||
return printer.PrintObj, nil
|
return printer.PrintObj, nil
|
||||||
}
|
}
|
||||||
|
@ -196,6 +215,10 @@ func (g *CommandGetOptions) Run(karmadaConfig KarmadaConfig, cmd *cobra.Command,
|
||||||
clusterInfos := make(map[string]*ClusterInfo)
|
clusterInfos := make(map[string]*ClusterInfo)
|
||||||
RBInfo = make(map[string]*OtherPrint)
|
RBInfo = make(map[string]*OtherPrint)
|
||||||
|
|
||||||
|
if g.AllNamespaces {
|
||||||
|
g.ExplicitNamespace = false
|
||||||
|
}
|
||||||
|
|
||||||
karmadaclient, err := clusterInfoInit(g, karmadaConfig, clusterInfos)
|
karmadaclient, err := clusterInfoInit(g, karmadaConfig, clusterInfos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -339,6 +362,13 @@ func (g *CommandGetOptions) getObjInfo(wg *sync.WaitGroup, mux *sync.Mutex, f cm
|
||||||
|
|
||||||
r.IgnoreErrors(apierrors.IsNotFound)
|
r.IgnoreErrors(apierrors.IsNotFound)
|
||||||
|
|
||||||
|
if !g.IsHumanReadablePrinter {
|
||||||
|
err := g.printGeneric(r)
|
||||||
|
|
||||||
|
*allErrs = append(*allErrs, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
infos, err := r.Infos()
|
infos, err := r.Infos()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
*allErrs = append(*allErrs, err)
|
*allErrs = append(*allErrs, err)
|
||||||
|
@ -386,6 +416,110 @@ func (g *CommandGetOptions) reconstructionRow(objs []Obj, table *metav1.Table) (
|
||||||
return allTableRows, mapping, nil
|
return allTableRows, mapping, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *CommandGetOptions) printGeneric(r *resource.Result) error {
|
||||||
|
// we flattened the data from the builder, so we have individual items, but now we'd like to either:
|
||||||
|
// 1. if there is more than one item, combine them all into a single list
|
||||||
|
// 2. if there is a single item and that item is a list, leave it as its specific list
|
||||||
|
// 3. if there is a single item and it is not a list, leave it as a single item
|
||||||
|
var errs []error
|
||||||
|
singleItemImplied := false
|
||||||
|
|
||||||
|
infos, err := g.extractInfosFromResource(r, &errs, &singleItemImplied)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
printer, err := g.ToPrinter(nil, nil, false, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var obj runtime.Object
|
||||||
|
if !singleItemImplied || len(infos) != 1 {
|
||||||
|
// we have zero or multple items, so coerce all items into a list.
|
||||||
|
// we don't want an *unstructured.Unstructured list yet, as we
|
||||||
|
// may be dealing with non-unstructured objects. Compose all items
|
||||||
|
// into an corev1.List, and then decode using an unstructured scheme.
|
||||||
|
list := corev1.List{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "List",
|
||||||
|
APIVersion: "v1",
|
||||||
|
},
|
||||||
|
ListMeta: metav1.ListMeta{},
|
||||||
|
}
|
||||||
|
for _, info := range infos {
|
||||||
|
list.Items = append(list.Items, runtime.RawExtension{Object: info.Object})
|
||||||
|
}
|
||||||
|
|
||||||
|
listData, err := json.Marshal(list)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
converted, err := runtime.Decode(unstructured.UnstructuredJSONScheme, listData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = converted
|
||||||
|
} else {
|
||||||
|
obj = infos[0].Object
|
||||||
|
}
|
||||||
|
|
||||||
|
isList := meta.IsListType(obj)
|
||||||
|
if isList {
|
||||||
|
items, err := meta.ExtractList(obj)
|
||||||
|
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(obj); 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))
|
||||||
|
}
|
||||||
|
if err := printer.PrintObj(list, g.Out); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if printErr := printer.PrintObj(obj, g.Out); printErr != nil {
|
||||||
|
errs = append(errs, printErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *CommandGetOptions) extractInfosFromResource(r *resource.Result, errs *[]error, singleItemImplied *bool) ([]*resource.Info, error) {
|
||||||
|
infos, err := r.IntoSingleItemImplied(singleItemImplied).Infos()
|
||||||
|
if err != nil {
|
||||||
|
if *singleItemImplied {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
*errs = append(*errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(infos) == 0 && g.IgnoreNotFound {
|
||||||
|
return nil, utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(*errs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return infos, nil
|
||||||
|
}
|
||||||
|
|
||||||
type trackingWriterWrapper struct {
|
type trackingWriterWrapper struct {
|
||||||
Delegate io.Writer
|
Delegate io.Writer
|
||||||
Written int
|
Written int
|
||||||
|
@ -461,6 +595,10 @@ func getFactory(clusterName string, clusterInfos map[string]*ClusterInfo) cmduti
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *CommandGetOptions) transformRequests(req *rest.Request) {
|
func (g *CommandGetOptions) transformRequests(req *rest.Request) {
|
||||||
|
if !g.ServerPrint || !g.IsHumanReadablePrinter {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
req.SetHeader("Accept", strings.Join([]string{
|
req.SetHeader("Accept", strings.Join([]string{
|
||||||
fmt.Sprintf("application/json;as=Table;v=%s;g=%s", metav1.SchemeGroupVersion.Version, metav1.GroupName),
|
fmt.Sprintf("application/json;as=Table;v=%s;g=%s", metav1.SchemeGroupVersion.Version, metav1.GroupName),
|
||||||
fmt.Sprintf("application/json;as=Table;v=%s;g=%s", metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName),
|
fmt.Sprintf("application/json;as=Table;v=%s;g=%s", metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName),
|
||||||
|
@ -615,7 +753,10 @@ func getExample(parentCommand string) string {
|
||||||
fmt.Sprintf("%s get replicasets nginx", parentCommand) + `
|
fmt.Sprintf("%s get replicasets nginx", parentCommand) + `
|
||||||
|
|
||||||
# List deployments in JSON output format, in the "v1" version of the "apps" API group ` + "\n" +
|
# List deployments in JSON output format, in the "v1" version of the "apps" API group ` + "\n" +
|
||||||
fmt.Sprintf("%s get deployments.v1.apps ", parentCommand) + `
|
fmt.Sprintf("%s get deployments.v1.apps -o json", parentCommand) + `
|
||||||
|
|
||||||
|
# Return only the phase value of the specified resource ` + "\n" +
|
||||||
|
fmt.Sprintf("%s get -o template deployment/nginx -C member1 --template={{.spec.replicas}}", parentCommand) + `
|
||||||
|
|
||||||
# List all replication controllers and services together in ps output format ` + "\n" +
|
# List all replication controllers and services together in ps output format ` + "\n" +
|
||||||
fmt.Sprintf("%s get rs,services", parentCommand) + `
|
fmt.Sprintf("%s get rs,services", parentCommand) + `
|
||||||
|
|
Loading…
Reference in New Issue