mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 23:15:14 +00:00
Merge pull request #79345 from liggitt/kubectl-printing
Add meta Table tests, fix --watch-only with single item
This commit is contained in:
commit
4745946525
@ -22,6 +22,7 @@ go_library(
|
|||||||
"get.go",
|
"get.go",
|
||||||
"get_flags.go",
|
"get_flags.go",
|
||||||
"humanreadable_flags.go",
|
"humanreadable_flags.go",
|
||||||
|
"skip_printer.go",
|
||||||
"sorter.go",
|
"sorter.go",
|
||||||
"table_printer.go",
|
"table_printer.go",
|
||||||
],
|
],
|
||||||
@ -60,6 +61,7 @@ go_library(
|
|||||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
"//vendor/k8s.io/utils/integer:go_default_library",
|
"//vendor/k8s.io/utils/integer:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/pointer:go_default_library",
|
||||||
"//vendor/vbom.ml/util/sortorder:go_default_library",
|
"//vendor/vbom.ml/util/sortorder:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -97,7 +99,6 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||||
|
@ -49,12 +49,13 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||||
|
utilpointer "k8s.io/utils/pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetOptions contains the input to the get command.
|
// GetOptions contains the input to the get command.
|
||||||
type GetOptions struct {
|
type GetOptions struct {
|
||||||
PrintFlags *PrintFlags
|
PrintFlags *PrintFlags
|
||||||
ToPrinter func(*meta.RESTMapping, bool, bool) (printers.ResourcePrinterFunc, error)
|
ToPrinter func(*meta.RESTMapping, *bool, bool, bool) (printers.ResourcePrinterFunc, error)
|
||||||
IsHumanReadablePrinter bool
|
IsHumanReadablePrinter bool
|
||||||
PrintWithOpenAPICols bool
|
PrintWithOpenAPICols bool
|
||||||
|
|
||||||
@ -230,7 +231,7 @@ func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
|
|||||||
o.IsHumanReadablePrinter = true
|
o.IsHumanReadablePrinter = true
|
||||||
}
|
}
|
||||||
|
|
||||||
o.ToPrinter = func(mapping *meta.RESTMapping, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) {
|
o.ToPrinter = func(mapping *meta.RESTMapping, outputObjects *bool, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) {
|
||||||
// make a new copy of current flags / opts before mutating
|
// make a new copy of current flags / opts before mutating
|
||||||
printFlags := o.PrintFlags.Copy()
|
printFlags := o.PrintFlags.Copy()
|
||||||
|
|
||||||
@ -257,6 +258,9 @@ func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
|
|||||||
if o.Sort {
|
if o.Sort {
|
||||||
printer = &SortingPrinter{Delegate: printer, SortField: sortBy}
|
printer = &SortingPrinter{Delegate: printer, SortField: sortBy}
|
||||||
}
|
}
|
||||||
|
if outputObjects != nil {
|
||||||
|
printer = &skipPrinter{delegate: printer, output: outputObjects}
|
||||||
|
}
|
||||||
if o.ServerPrint {
|
if o.ServerPrint {
|
||||||
printer = &TablePrinter{Delegate: printer}
|
printer = &TablePrinter{Delegate: printer}
|
||||||
}
|
}
|
||||||
@ -539,7 +543,7 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
|
|||||||
fmt.Fprintln(o.ErrOut)
|
fmt.Fprintln(o.ErrOut)
|
||||||
}
|
}
|
||||||
|
|
||||||
printer, err = o.ToPrinter(mapping, printWithNamespace, printWithKind)
|
printer, err = o.ToPrinter(mapping, nil, printWithNamespace, printWithKind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.Has(err.Error()) {
|
if !errs.Has(err.Error()) {
|
||||||
errs.Insert(err.Error())
|
errs.Insert(err.Error())
|
||||||
@ -635,7 +639,8 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
|
|||||||
|
|
||||||
info := infos[0]
|
info := infos[0]
|
||||||
mapping := info.ResourceMapping()
|
mapping := info.ResourceMapping()
|
||||||
printer, err := o.ToPrinter(mapping, o.AllNamespaces, false)
|
outputObjects := utilpointer.BoolPtr(!o.WatchOnly)
|
||||||
|
printer, err := o.ToPrinter(mapping, outputObjects, o.AllNamespaces, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -664,25 +669,29 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
|
|||||||
tableGK := metainternal.SchemeGroupVersion.WithKind("Table").GroupKind()
|
tableGK := metainternal.SchemeGroupVersion.WithKind("Table").GroupKind()
|
||||||
|
|
||||||
// print the current object
|
// print the current object
|
||||||
if !o.WatchOnly {
|
var objsToPrint []runtime.Object
|
||||||
var objsToPrint []runtime.Object
|
if isList {
|
||||||
|
objsToPrint, _ = meta.ExtractList(obj)
|
||||||
if isList {
|
} else {
|
||||||
objsToPrint, _ = meta.ExtractList(obj)
|
objsToPrint = append(objsToPrint, obj)
|
||||||
} else {
|
}
|
||||||
objsToPrint = append(objsToPrint, obj)
|
for _, objToPrint := range objsToPrint {
|
||||||
|
if o.IsHumanReadablePrinter && objToPrint.GetObjectKind().GroupVersionKind().GroupKind() != tableGK {
|
||||||
|
// printing anything other than tables always takes the internal version, but the watch event uses externals
|
||||||
|
internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
|
||||||
|
objToPrint = attemptToConvertToInternal(objToPrint, legacyscheme.Scheme, internalGV)
|
||||||
}
|
}
|
||||||
for _, objToPrint := range objsToPrint {
|
if err := printer.PrintObj(objToPrint, writer); err != nil {
|
||||||
if o.IsHumanReadablePrinter && objToPrint.GetObjectKind().GroupVersionKind().GroupKind() != tableGK {
|
return fmt.Errorf("unable to output the provided object: %v", err)
|
||||||
// printing anything other than tables always takes the internal version, but the watch event uses externals
|
|
||||||
internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
|
|
||||||
objToPrint = attemptToConvertToInternal(objToPrint, legacyscheme.Scheme, internalGV)
|
|
||||||
}
|
|
||||||
if err := printer.PrintObj(objToPrint, writer); err != nil {
|
|
||||||
return fmt.Errorf("unable to output the provided object: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
writer.Flush()
|
}
|
||||||
|
writer.Flush()
|
||||||
|
if isList {
|
||||||
|
// we can start outputting objects now, watches started from lists don't emit synthetic added events
|
||||||
|
*outputObjects = true
|
||||||
|
} else {
|
||||||
|
// suppress output, since watches started for individual items emit a synthetic ADDED event first
|
||||||
|
*outputObjects = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// print watched changes
|
// print watched changes
|
||||||
@ -691,18 +700,11 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
first := true
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
intr := interrupt.New(nil, cancel)
|
intr := interrupt.New(nil, cancel)
|
||||||
intr.Run(func() error {
|
intr.Run(func() error {
|
||||||
_, err := watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) {
|
_, err := watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) {
|
||||||
if !isList && first {
|
|
||||||
// drop the initial watch event in the single resource case
|
|
||||||
first = false
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// printing always takes the internal version, but the watch event uses externals
|
// printing always takes the internal version, but the watch event uses externals
|
||||||
// TODO fix printing to use server-side or be version agnostic
|
// TODO fix printing to use server-side or be version agnostic
|
||||||
objToPrint := e.Object
|
objToPrint := e.Object
|
||||||
@ -714,6 +716,8 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
writer.Flush()
|
writer.Flush()
|
||||||
|
// after processing at least one event, start outputting objects
|
||||||
|
*outputObjects = true
|
||||||
return false, nil
|
return false, nil
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
@ -750,7 +754,7 @@ func (o *GetOptions) printGeneric(r *resource.Result) error {
|
|||||||
return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
|
return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
|
||||||
}
|
}
|
||||||
|
|
||||||
printer, err := o.ToPrinter(nil, false, false)
|
printer, err := o.ToPrinter(nil, nil, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,6 @@ import (
|
|||||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/runtime/serializer/json"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
|
"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
|
||||||
"k8s.io/apimachinery/pkg/util/diff"
|
"k8s.io/apimachinery/pkg/util/diff"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
@ -244,6 +243,31 @@ foo 0/0 0 <unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetTableObjects(t *testing.T) {
|
||||||
|
pods, _, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Resp: &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items[0])},
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
cmd.Run(cmd, []string{"pods", "foo"})
|
||||||
|
|
||||||
|
expected := `NAME READY STATUS RESTARTS AGE
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetObjectsShowKind(t *testing.T) {
|
func TestGetObjectsShowKind(t *testing.T) {
|
||||||
pods, _, _ := cmdtesting.TestData()
|
pods, _, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
@ -270,6 +294,32 @@ pod/foo 0/0 0 <unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetTableObjectsShowKind(t *testing.T) {
|
||||||
|
pods, _, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Resp: &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items[0])},
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
cmd.Flags().Set("show-kind", "true")
|
||||||
|
cmd.Run(cmd, []string{"pods", "foo"})
|
||||||
|
|
||||||
|
expected := `NAME READY STATUS RESTARTS AGE
|
||||||
|
pod/foo 0/0 0 <unknown>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetMultipleResourceTypesShowKinds(t *testing.T) {
|
func TestGetMultipleResourceTypesShowKinds(t *testing.T) {
|
||||||
pods, svcs, _ := cmdtesting.TestData()
|
pods, svcs, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
@ -325,6 +375,61 @@ service/baz ClusterIP <none> <none> <none> <unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetMultipleTableResourceTypesShowKinds(t *testing.T) {
|
||||||
|
pods, svcs, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
switch p, m := req.URL.Path, req.Method; {
|
||||||
|
case p == "/namespaces/test/pods" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items...)}, nil
|
||||||
|
case p == "/namespaces/test/replicationcontrollers" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &corev1.ReplicationControllerList{})}, nil
|
||||||
|
case p == "/namespaces/test/services" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: serviceTableObjBody(codec, svcs.Items...)}, nil
|
||||||
|
case p == "/namespaces/test/statefulsets" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &appsv1.StatefulSetList{})}, nil
|
||||||
|
case p == "/namespaces/test/horizontalpodautoscalers" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &autoscalingv1.HorizontalPodAutoscalerList{})}, nil
|
||||||
|
case p == "/namespaces/test/jobs" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &batchv1.JobList{})}, nil
|
||||||
|
case p == "/namespaces/test/cronjobs" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &batchv1beta1.CronJobList{})}, nil
|
||||||
|
case p == "/namespaces/test/daemonsets" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &appsv1.DaemonSetList{})}, nil
|
||||||
|
case p == "/namespaces/test/deployments" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &extensionsv1beta1.DeploymentList{})}, nil
|
||||||
|
case p == "/namespaces/test/replicasets" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &extensionsv1beta1.ReplicaSetList{})}, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
cmd.Run(cmd, []string{"all"})
|
||||||
|
|
||||||
|
expected := `NAME READY STATUS RESTARTS AGE
|
||||||
|
pod/foo 0/0 0 <unknown>
|
||||||
|
pod/bar 0/0 0 <unknown>
|
||||||
|
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||||
|
service/baz ClusterIP <none> <none> <none> <unknown>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetObjectsShowLabels(t *testing.T) {
|
func TestGetObjectsShowLabels(t *testing.T) {
|
||||||
pods, _, _ := cmdtesting.TestData()
|
pods, _, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
@ -351,6 +456,32 @@ foo 0/0 0 <unknown> <none>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetTableObjectsShowLabels(t *testing.T) {
|
||||||
|
pods, _, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Resp: &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items[0])},
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
cmd.Flags().Set("show-labels", "true")
|
||||||
|
cmd.Run(cmd, []string{"pods", "foo"})
|
||||||
|
|
||||||
|
expected := `NAME READY STATUS RESTARTS AGE LABELS
|
||||||
|
foo 0/0 0 <unknown> <none>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetEmptyTable(t *testing.T) {
|
func TestGetEmptyTable(t *testing.T) {
|
||||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
defer tf.Cleanup()
|
defer tf.Cleanup()
|
||||||
@ -769,6 +900,32 @@ foo 0/0 0 <unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetTableObjectsIdentifiedByFile(t *testing.T) {
|
||||||
|
pods, _, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Resp: &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items[0])},
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
cmd.Flags().Set("filename", "../../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml")
|
||||||
|
cmd.Run(cmd, []string{})
|
||||||
|
|
||||||
|
expected := `NAME READY STATUS RESTARTS AGE
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetListObjects(t *testing.T) {
|
func TestGetListObjects(t *testing.T) {
|
||||||
pods, _, _ := cmdtesting.TestData()
|
pods, _, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
@ -795,6 +952,32 @@ bar 0/0 0 <unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetListTableObjects(t *testing.T) {
|
||||||
|
pods, _, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Resp: &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items...)},
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
cmd.Run(cmd, []string{"pods"})
|
||||||
|
|
||||||
|
expected := `NAME READY STATUS RESTARTS AGE
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
bar 0/0 0 <unknown>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetListComponentStatus(t *testing.T) {
|
func TestGetListComponentStatus(t *testing.T) {
|
||||||
statuses := testComponentStatusData()
|
statuses := testComponentStatusData()
|
||||||
|
|
||||||
@ -922,6 +1105,44 @@ service/baz ClusterIP <none> <none> <none> <unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetMultipleTypeTableObjects(t *testing.T) {
|
||||||
|
pods, svc, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
switch req.URL.Path {
|
||||||
|
case "/namespaces/test/pods":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items...)}, nil
|
||||||
|
case "/namespaces/test/services":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: serviceTableObjBody(codec, svc.Items...)}, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
cmd.Run(cmd, []string{"pods,services"})
|
||||||
|
|
||||||
|
expected := `NAME READY STATUS RESTARTS AGE
|
||||||
|
pod/foo 0/0 0 <unknown>
|
||||||
|
pod/bar 0/0 0 <unknown>
|
||||||
|
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||||
|
service/baz ClusterIP <none> <none> <none> <unknown>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetMultipleTypeObjectsAsList(t *testing.T) {
|
func TestGetMultipleTypeObjectsAsList(t *testing.T) {
|
||||||
pods, svc, _ := cmdtesting.TestData()
|
pods, svc, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
@ -1066,6 +1287,49 @@ service/baz ClusterIP <none> <none> <none> <unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetMultipleTypeTableObjectsWithLabelSelector(t *testing.T) {
|
||||||
|
pods, svc, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
if req.URL.Query().Get(metav1.LabelSelectorQueryParam("v1")) != "a=b" {
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
}
|
||||||
|
switch req.URL.Path {
|
||||||
|
case "/namespaces/test/pods":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items...)}, nil
|
||||||
|
case "/namespaces/test/services":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: serviceTableObjBody(codec, svc.Items...)}, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
|
||||||
|
cmd.Flags().Set("selector", "a=b")
|
||||||
|
cmd.Run(cmd, []string{"pods,services"})
|
||||||
|
|
||||||
|
expected := `NAME READY STATUS RESTARTS AGE
|
||||||
|
pod/foo 0/0 0 <unknown>
|
||||||
|
pod/bar 0/0 0 <unknown>
|
||||||
|
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||||
|
service/baz ClusterIP <none> <none> <none> <unknown>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetMultipleTypeObjectsWithFieldSelector(t *testing.T) {
|
func TestGetMultipleTypeObjectsWithFieldSelector(t *testing.T) {
|
||||||
pods, svc, _ := cmdtesting.TestData()
|
pods, svc, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
@ -1109,6 +1373,49 @@ service/baz ClusterIP <none> <none> <none> <unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetMultipleTypeTableObjectsWithFieldSelector(t *testing.T) {
|
||||||
|
pods, svc, _ := cmdtesting.TestData()
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
if req.URL.Query().Get(metav1.FieldSelectorQueryParam("v1")) != "a=b" {
|
||||||
|
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||||
|
}
|
||||||
|
switch req.URL.Path {
|
||||||
|
case "/namespaces/test/pods":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items...)}, nil
|
||||||
|
case "/namespaces/test/services":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: serviceTableObjBody(codec, svc.Items...)}, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
|
||||||
|
cmd.Flags().Set("field-selector", "a=b")
|
||||||
|
cmd.Run(cmd, []string{"pods,services"})
|
||||||
|
|
||||||
|
expected := `NAME READY STATUS RESTARTS AGE
|
||||||
|
pod/foo 0/0 0 <unknown>
|
||||||
|
pod/bar 0/0 0 <unknown>
|
||||||
|
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||||
|
service/baz ClusterIP <none> <none> <none> <unknown>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) {
|
func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) {
|
||||||
_, svc, _ := cmdtesting.TestData()
|
_, svc, _ := cmdtesting.TestData()
|
||||||
node := &corev1.Node{
|
node := &corev1.Node{
|
||||||
@ -1152,6 +1459,49 @@ node/foo Unknown <none> <unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetMultipleTypeTableObjectsWithDirectReference(t *testing.T) {
|
||||||
|
_, svc, _ := cmdtesting.TestData()
|
||||||
|
node := &corev1.Node{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
switch req.URL.Path {
|
||||||
|
case "/nodes/foo":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: nodeTableObjBody(codec, *node)}, nil
|
||||||
|
case "/namespaces/test/services/bar":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: serviceTableObjBody(codec, svc.Items[0])}, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
|
||||||
|
cmd.Run(cmd, []string{"services/bar", "node/foo"})
|
||||||
|
|
||||||
|
expected := `NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||||
|
service/baz ClusterIP <none> <none> <none> <unknown>
|
||||||
|
NAME STATUS ROLES AGE VERSION
|
||||||
|
node/foo Unknown <none> <unknown>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func watchTestData() ([]corev1.Pod, []watch.Event) {
|
func watchTestData() ([]corev1.Pod, []watch.Event) {
|
||||||
pods := []corev1.Pod{
|
pods := []corev1.Pod{
|
||||||
{
|
{
|
||||||
@ -1309,6 +1659,57 @@ foo 0/0 0 <unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWatchTableLabelSelector(t *testing.T) {
|
||||||
|
pods, events := watchTestData()
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
podList := &corev1.PodList{
|
||||||
|
Items: pods,
|
||||||
|
ListMeta: metav1.ListMeta{
|
||||||
|
ResourceVersion: "10",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
if req.URL.Query().Get(metav1.LabelSelectorQueryParam("v1")) != "a=b" {
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
}
|
||||||
|
switch req.URL.Path {
|
||||||
|
case "/namespaces/test/pods":
|
||||||
|
if req.URL.Query().Get("watch") == "true" {
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableWatchBody(codec, events[2:])}, nil
|
||||||
|
}
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, podList.Items...)}, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
|
||||||
|
cmd.Flags().Set("watch", "true")
|
||||||
|
cmd.Flags().Set("selector", "a=b")
|
||||||
|
cmd.Run(cmd, []string{"pods"})
|
||||||
|
|
||||||
|
expected := `NAME READY STATUS RESTARTS AGE
|
||||||
|
bar 0/0 0 <unknown>
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestWatchFieldSelector(t *testing.T) {
|
func TestWatchFieldSelector(t *testing.T) {
|
||||||
pods, events := watchTestData()
|
pods, events := watchTestData()
|
||||||
|
|
||||||
@ -1360,6 +1761,57 @@ foo 0/0 0 <unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWatchTableFieldSelector(t *testing.T) {
|
||||||
|
pods, events := watchTestData()
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
podList := &corev1.PodList{
|
||||||
|
Items: pods,
|
||||||
|
ListMeta: metav1.ListMeta{
|
||||||
|
ResourceVersion: "10",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
if req.URL.Query().Get(metav1.FieldSelectorQueryParam("v1")) != "a=b" {
|
||||||
|
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||||
|
}
|
||||||
|
switch req.URL.Path {
|
||||||
|
case "/namespaces/test/pods":
|
||||||
|
if req.URL.Query().Get("watch") == "true" {
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableWatchBody(codec, events[2:])}, nil
|
||||||
|
}
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, podList.Items...)}, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
|
||||||
|
cmd.Flags().Set("watch", "true")
|
||||||
|
cmd.Flags().Set("field-selector", "a=b")
|
||||||
|
cmd.Run(cmd, []string{"pods"})
|
||||||
|
|
||||||
|
expected := `NAME READY STATUS RESTARTS AGE
|
||||||
|
bar 0/0 0 <unknown>
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestWatchResource(t *testing.T) {
|
func TestWatchResource(t *testing.T) {
|
||||||
pods, events := watchTestData()
|
pods, events := watchTestData()
|
||||||
|
|
||||||
@ -1403,6 +1855,49 @@ foo 0/0 0 <unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWatchTableResource(t *testing.T) {
|
||||||
|
pods, events := watchTestData()
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
switch req.URL.Path {
|
||||||
|
case "/namespaces/test/pods/foo":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods[1])}, nil
|
||||||
|
case "/namespaces/test/pods":
|
||||||
|
if req.URL.Query().Get("watch") == "true" && req.URL.Query().Get("fieldSelector") == "metadata.name=foo" {
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableWatchBody(codec, events[1:])}, nil
|
||||||
|
}
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
|
||||||
|
cmd.Flags().Set("watch", "true")
|
||||||
|
cmd.Run(cmd, []string{"pods", "foo"})
|
||||||
|
|
||||||
|
expected := `NAME READY STATUS RESTARTS AGE
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestWatchResourceTable(t *testing.T) {
|
func TestWatchResourceTable(t *testing.T) {
|
||||||
columns := []metav1beta1.TableColumnDefinition{
|
columns := []metav1beta1.TableColumnDefinition{
|
||||||
{Name: "Name", Type: "string", Format: "name", Description: "the name", Priority: 0},
|
{Name: "Name", Type: "string", Format: "name", Description: "the name", Priority: 0},
|
||||||
@ -1596,6 +2091,48 @@ foo 0/0 0 <unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWatchOnlyTableResource(t *testing.T) {
|
||||||
|
pods, events := watchTestData()
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
switch req.URL.Path {
|
||||||
|
case "/namespaces/test/pods/foo":
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods[1])}, nil
|
||||||
|
case "/namespaces/test/pods":
|
||||||
|
if req.URL.Query().Get("watch") == "true" && req.URL.Query().Get("fieldSelector") == "metadata.name=foo" {
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableWatchBody(codec, events[1:])}, nil
|
||||||
|
}
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
|
||||||
|
cmd.Flags().Set("watch-only", "true")
|
||||||
|
cmd.Run(cmd, []string{"pods", "foo"})
|
||||||
|
|
||||||
|
expected := `NAME READY STATUS RESTARTS AGE
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestWatchOnlyList(t *testing.T) {
|
func TestWatchOnlyList(t *testing.T) {
|
||||||
pods, events := watchTestData()
|
pods, events := watchTestData()
|
||||||
|
|
||||||
@ -1641,6 +2178,51 @@ foo 0/0 0 <unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWatchOnlyTableList(t *testing.T) {
|
||||||
|
pods, events := watchTestData()
|
||||||
|
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
defer tf.Cleanup()
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
podList := &corev1.PodList{
|
||||||
|
Items: pods,
|
||||||
|
ListMeta: metav1.ListMeta{
|
||||||
|
ResourceVersion: "10",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
switch req.URL.Path {
|
||||||
|
case "/namespaces/test/pods":
|
||||||
|
if req.URL.Query().Get("watch") == "true" {
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableWatchBody(codec, events[2:])}, nil
|
||||||
|
}
|
||||||
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, podList.Items...)}, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
|
||||||
|
cmd.Flags().Set("watch-only", "true")
|
||||||
|
cmd.Run(cmd, []string{"pods"})
|
||||||
|
|
||||||
|
expected := `NAME READY STATUS RESTARTS AGE
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
foo 0/0 0 <unknown>
|
||||||
|
`
|
||||||
|
if e, a := expected, buf.String(); e != a {
|
||||||
|
t.Errorf("expected\n%v\ngot\n%v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func watchBody(codec runtime.Codec, events []watch.Event) io.ReadCloser {
|
func watchBody(codec runtime.Codec, events []watch.Event) io.ReadCloser {
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
enc := restclientwatch.NewEncoder(streaming.NewEncoder(buf, codec), codec)
|
enc := restclientwatch.NewEncoder(streaming.NewEncoder(buf, codec), codec)
|
||||||
@ -1649,5 +2231,101 @@ func watchBody(codec runtime.Codec, events []watch.Event) io.ReadCloser {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return json.Framer.NewFrameReader(ioutil.NopCloser(buf))
|
return ioutil.NopCloser(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
var podColumns = []metav1.TableColumnDefinition{
|
||||||
|
{Name: "Name", Type: "string", Format: "name"},
|
||||||
|
{Name: "Ready", Type: "string", Format: ""},
|
||||||
|
{Name: "Status", Type: "string", Format: ""},
|
||||||
|
{Name: "Restarts", Type: "integer", Format: ""},
|
||||||
|
{Name: "Age", Type: "string", Format: ""},
|
||||||
|
{Name: "IP", Type: "string", Format: "", Priority: 1},
|
||||||
|
{Name: "Node", Type: "string", Format: "", Priority: 1},
|
||||||
|
{Name: "Nominated Node", Type: "string", Format: "", Priority: 1},
|
||||||
|
{Name: "Readiness Gates", Type: "string", Format: "", Priority: 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
// build a meta table response from a pod list
|
||||||
|
func podTableObjBody(codec runtime.Codec, pods ...corev1.Pod) io.ReadCloser {
|
||||||
|
table := &metav1.Table{
|
||||||
|
ColumnDefinitions: podColumns,
|
||||||
|
}
|
||||||
|
for i := range pods {
|
||||||
|
b := bytes.NewBuffer(nil)
|
||||||
|
codec.Encode(&pods[i], b)
|
||||||
|
table.Rows = append(table.Rows, metav1.TableRow{
|
||||||
|
Object: runtime.RawExtension{Raw: b.Bytes()},
|
||||||
|
Cells: []interface{}{pods[i].Name, "0/0", "", int64(0), "<unknown>", "<none>", "<none>", "<none>", "<none>"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return cmdtesting.ObjBody(codec, table)
|
||||||
|
}
|
||||||
|
|
||||||
|
// build meta table watch events from pod watch events
|
||||||
|
func podTableWatchBody(codec runtime.Codec, events []watch.Event) io.ReadCloser {
|
||||||
|
tableEvents := []watch.Event{}
|
||||||
|
for i, e := range events {
|
||||||
|
b := bytes.NewBuffer(nil)
|
||||||
|
codec.Encode(e.Object, b)
|
||||||
|
var columns []metav1.TableColumnDefinition
|
||||||
|
if i == 0 {
|
||||||
|
columns = podColumns
|
||||||
|
}
|
||||||
|
tableEvents = append(tableEvents, watch.Event{
|
||||||
|
Type: e.Type,
|
||||||
|
Object: &metav1.Table{
|
||||||
|
ColumnDefinitions: columns,
|
||||||
|
Rows: []metav1.TableRow{{
|
||||||
|
Object: runtime.RawExtension{Raw: b.Bytes()},
|
||||||
|
Cells: []interface{}{e.Object.(*corev1.Pod).Name, "0/0", "", int64(0), "<unknown>", "<none>", "<none>", "<none>", "<none>"},
|
||||||
|
}}},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return watchBody(codec, tableEvents)
|
||||||
|
}
|
||||||
|
|
||||||
|
// build a meta table response from a service list
|
||||||
|
func serviceTableObjBody(codec runtime.Codec, services ...corev1.Service) io.ReadCloser {
|
||||||
|
table := &metav1.Table{
|
||||||
|
ColumnDefinitions: []metav1.TableColumnDefinition{
|
||||||
|
{Name: "Name", Type: "string", Format: "name"},
|
||||||
|
{Name: "Type", Type: "string", Format: ""},
|
||||||
|
{Name: "Cluster-IP", Type: "string", Format: ""},
|
||||||
|
{Name: "External-IP", Type: "string", Format: ""},
|
||||||
|
{Name: "Port(s)", Type: "string", Format: ""},
|
||||||
|
{Name: "Age", Type: "string", Format: ""},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := range services {
|
||||||
|
b := bytes.NewBuffer(nil)
|
||||||
|
codec.Encode(&services[i], b)
|
||||||
|
table.Rows = append(table.Rows, metav1.TableRow{
|
||||||
|
Object: runtime.RawExtension{Raw: b.Bytes()},
|
||||||
|
Cells: []interface{}{services[i].Name, "ClusterIP", "<none>", "<none>", "<none>", "<unknown>"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return cmdtesting.ObjBody(codec, table)
|
||||||
|
}
|
||||||
|
|
||||||
|
// build a meta table response from a node list
|
||||||
|
func nodeTableObjBody(codec runtime.Codec, nodes ...corev1.Node) io.ReadCloser {
|
||||||
|
table := &metav1.Table{
|
||||||
|
ColumnDefinitions: []metav1.TableColumnDefinition{
|
||||||
|
{Name: "Name", Type: "string", Format: "name"},
|
||||||
|
{Name: "Status", Type: "string", Format: ""},
|
||||||
|
{Name: "Roles", Type: "string", Format: ""},
|
||||||
|
{Name: "Age", Type: "string", Format: ""},
|
||||||
|
{Name: "Version", Type: "string", Format: ""},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := range nodes {
|
||||||
|
b := bytes.NewBuffer(nil)
|
||||||
|
codec.Encode(&nodes[i], b)
|
||||||
|
table.Rows = append(table.Rows, metav1.TableRow{
|
||||||
|
Object: runtime.RawExtension{Raw: b.Bytes()},
|
||||||
|
Cells: []interface{}{nodes[i].Name, "Unknown", "<none>", "<unknown>", ""},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return cmdtesting.ObjBody(codec, table)
|
||||||
}
|
}
|
||||||
|
@ -18,26 +18,58 @@ package get
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
|
func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
|
||||||
testObject := &api.Pod{ObjectMeta: metav1.ObjectMeta{
|
testTable := &metav1.Table{
|
||||||
Name: "foo",
|
ColumnDefinitions: []metav1.TableColumnDefinition{
|
||||||
Labels: map[string]string{
|
{Name: "Name", Type: "string", Format: "name"},
|
||||||
"l1": "value",
|
{Name: "Ready", Type: "string", Format: ""},
|
||||||
|
{Name: "Status", Type: "string", Format: ""},
|
||||||
|
{Name: "Restarts", Type: "integer", Format: ""},
|
||||||
|
{Name: "Age", Type: "string", Format: ""},
|
||||||
|
{Name: "IP", Type: "string", Format: "", Priority: 1},
|
||||||
|
{Name: "Node", Type: "string", Format: "", Priority: 1},
|
||||||
|
{Name: "Nominated Node", Type: "string", Format: "", Priority: 1},
|
||||||
|
{Name: "Readiness Gates", Type: "string", Format: "", Priority: 1},
|
||||||
},
|
},
|
||||||
}}
|
Rows: []metav1.TableRow{{
|
||||||
|
Object: runtime.RawExtension{
|
||||||
|
Object: &corev1.Pod{
|
||||||
|
TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Pod"},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{"l1": "value"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Cells: []interface{}{"foo", "0/0", "", int64(0), "<unknown>", "<none>", "<none>", "<none>", "<none>"},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
testPod := &api.Pod{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: "v1",
|
||||||
|
Kind: "Pod",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"l1": "value",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
|
testObject runtime.Object
|
||||||
showKind bool
|
showKind bool
|
||||||
showLabels bool
|
showLabels bool
|
||||||
|
|
||||||
@ -56,53 +88,104 @@ func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty output format matches a humanreadable printer",
|
name: "empty output format matches a humanreadable printer",
|
||||||
|
testObject: testPod.DeepCopy(),
|
||||||
|
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\nfoo\\ +0/0\\ +0\\ +<unknown>\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty output format matches a humanreadable printer",
|
||||||
|
testObject: testTable.DeepCopy(),
|
||||||
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\nfoo\\ +0/0\\ +0\\ +<unknown>\n",
|
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\nfoo\\ +0/0\\ +0\\ +<unknown>\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "\"wide\" output format prints",
|
name: "\"wide\" output format prints",
|
||||||
|
testObject: testPod.DeepCopy(),
|
||||||
|
outputFormat: "wide",
|
||||||
|
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +IP\\ +NODE\\ +NOMINATED NODE\\ +READINESS GATES\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\\ +<none>\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "\"wide\" output format prints",
|
||||||
|
testObject: testTable.DeepCopy(),
|
||||||
outputFormat: "wide",
|
outputFormat: "wide",
|
||||||
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +IP\\ +NODE\\ +NOMINATED NODE\\ +READINESS GATES\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\\ +<none>\n",
|
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +IP\\ +NODE\\ +NOMINATED NODE\\ +READINESS GATES\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\\ +<none>\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no-headers prints output with no headers",
|
name: "no-headers prints output with no headers",
|
||||||
|
testObject: testPod.DeepCopy(),
|
||||||
|
noHeaders: true,
|
||||||
|
expectedOutput: "foo\\ +0/0\\ +0\\ +<unknown>\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no-headers prints output with no headers",
|
||||||
|
testObject: testTable.DeepCopy(),
|
||||||
noHeaders: true,
|
noHeaders: true,
|
||||||
expectedOutput: "foo\\ +0/0\\ +0\\ +<unknown>\n",
|
expectedOutput: "foo\\ +0/0\\ +0\\ +<unknown>\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no-headers and a \"wide\" output format prints output with no headers and additional columns",
|
name: "no-headers and a \"wide\" output format prints output with no headers and additional columns",
|
||||||
|
testObject: testPod.DeepCopy(),
|
||||||
|
outputFormat: "wide",
|
||||||
|
noHeaders: true,
|
||||||
|
expectedOutput: "foo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\\ +<none>\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no-headers and a \"wide\" output format prints output with no headers and additional columns",
|
||||||
|
testObject: testTable.DeepCopy(),
|
||||||
outputFormat: "wide",
|
outputFormat: "wide",
|
||||||
noHeaders: true,
|
noHeaders: true,
|
||||||
expectedOutput: "foo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\\ +<none>\n",
|
expectedOutput: "foo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\\ +<none>\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "show-kind displays the resource's kind, even when printing a single type of resource",
|
name: "show-kind displays the resource's kind, even when printing a single type of resource",
|
||||||
|
testObject: testPod.DeepCopy(),
|
||||||
|
showKind: true,
|
||||||
|
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\npod/foo\\ +0/0\\ +0\\ +<unknown>\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "show-kind displays the resource's kind, even when printing a single type of resource",
|
||||||
|
testObject: testTable.DeepCopy(),
|
||||||
showKind: true,
|
showKind: true,
|
||||||
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\npod/foo\\ +0/0\\ +0\\ +<unknown>\n",
|
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\npod/foo\\ +0/0\\ +0\\ +<unknown>\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "label-columns prints specified label values in new column",
|
name: "label-columns prints specified label values in new column",
|
||||||
|
testObject: testPod.DeepCopy(),
|
||||||
|
columnLabels: []string{"l1"},
|
||||||
|
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +L1\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +value\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "label-columns prints specified label values in new column",
|
||||||
|
testObject: testTable.DeepCopy(),
|
||||||
columnLabels: []string{"l1"},
|
columnLabels: []string{"l1"},
|
||||||
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +L1\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +value\n",
|
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +L1\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +value\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "withNamespace displays an additional NAMESPACE column",
|
name: "withNamespace displays an additional NAMESPACE column",
|
||||||
|
testObject: testPod.DeepCopy(),
|
||||||
|
withNamespace: true,
|
||||||
|
expectedOutput: "NAMESPACE\\ +NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\n\\ +foo\\ +0/0\\ +0\\ +<unknown>\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "withNamespace displays an additional NAMESPACE column",
|
||||||
|
testObject: testTable.DeepCopy(),
|
||||||
withNamespace: true,
|
withNamespace: true,
|
||||||
expectedOutput: "NAMESPACE\\ +NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\n\\ +foo\\ +0/0\\ +0\\ +<unknown>\n",
|
expectedOutput: "NAMESPACE\\ +NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\n\\ +foo\\ +0/0\\ +0\\ +<unknown>\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no printer is matched on an invalid outputFormat",
|
name: "no printer is matched on an invalid outputFormat",
|
||||||
|
testObject: testPod.DeepCopy(),
|
||||||
outputFormat: "invalid",
|
outputFormat: "invalid",
|
||||||
expectNoMatch: true,
|
expectNoMatch: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "printer should not match on any other format supported by another printer",
|
name: "printer should not match on any other format supported by another printer",
|
||||||
|
testObject: testPod.DeepCopy(),
|
||||||
outputFormat: "go-template",
|
outputFormat: "go-template",
|
||||||
expectNoMatch: true,
|
expectNoMatch: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s %T", tc.name, tc.testObject), func(t *testing.T) {
|
||||||
printFlags := HumanPrintFlags{
|
printFlags := HumanPrintFlags{
|
||||||
ShowKind: &tc.showKind,
|
ShowKind: &tc.showKind,
|
||||||
ShowLabels: &tc.showLabels,
|
ShowLabels: &tc.showLabels,
|
||||||
@ -139,7 +222,7 @@ func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
out := bytes.NewBuffer([]byte{})
|
out := bytes.NewBuffer([]byte{})
|
||||||
err = p.PrintObj(testObject, out)
|
err = p.PrintObj(tc.testObject, out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -149,7 +232,7 @@ func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
|
|||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if !match {
|
if !match {
|
||||||
t.Errorf("unexpected output: expecting %q, got %q", tc.expectedOutput, out.String())
|
t.Errorf("unexpected output: expecting\n%s\ngot\n%s", tc.expectedOutput, out.String())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
48
pkg/kubectl/cmd/get/skip_printer.go
Normal file
48
pkg/kubectl/cmd/get/skip_printer.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 The Kubernetes Authors.
|
||||||
|
|
||||||
|
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 get
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/cli-runtime/pkg/printers"
|
||||||
|
)
|
||||||
|
|
||||||
|
// skipPrinter allows conditionally suppressing object output via the output field.
|
||||||
|
// table objects are suppressed by setting their Rows to nil (allowing column definitions to propagate to the delegate).
|
||||||
|
// non-table objects are suppressed by not calling the delegate at all.
|
||||||
|
type skipPrinter struct {
|
||||||
|
delegate printers.ResourcePrinter
|
||||||
|
output *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *skipPrinter) PrintObj(obj runtime.Object, writer io.Writer) error {
|
||||||
|
if *p.output {
|
||||||
|
return p.delegate.PrintObj(obj, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
table, isTable := obj.(*metav1beta1.Table)
|
||||||
|
if !isTable {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
table = table.DeepCopy()
|
||||||
|
table.Rows = nil
|
||||||
|
return p.delegate.PrintObj(table, writer)
|
||||||
|
}
|
@ -48,10 +48,11 @@ type handlerEntry struct {
|
|||||||
// will only be printed if the object type changes. This makes it useful for printing items
|
// will only be printed if the object type changes. This makes it useful for printing items
|
||||||
// received from watches.
|
// received from watches.
|
||||||
type HumanReadablePrinter struct {
|
type HumanReadablePrinter struct {
|
||||||
handlerMap map[reflect.Type]*handlerEntry
|
handlerMap map[reflect.Type]*handlerEntry
|
||||||
options PrintOptions
|
options PrintOptions
|
||||||
lastType interface{}
|
lastType interface{}
|
||||||
lastColumns []metav1beta1.TableColumnDefinition
|
lastColumns []metav1beta1.TableColumnDefinition
|
||||||
|
printedHeaders bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ TableGenerator = &HumanReadablePrinter{}
|
var _ TableGenerator = &HumanReadablePrinter{}
|
||||||
|
@ -80,18 +80,22 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er
|
|||||||
if table, ok := obj.(*metav1beta1.Table); ok {
|
if table, ok := obj.(*metav1beta1.Table); ok {
|
||||||
// Do not print headers if this table has no column definitions, or they are the same as the last ones we printed
|
// Do not print headers if this table has no column definitions, or they are the same as the last ones we printed
|
||||||
localOptions := h.options
|
localOptions := h.options
|
||||||
if len(table.ColumnDefinitions) == 0 || reflect.DeepEqual(table.ColumnDefinitions, h.lastColumns) {
|
if h.printedHeaders && (len(table.ColumnDefinitions) == 0 || reflect.DeepEqual(table.ColumnDefinitions, h.lastColumns)) {
|
||||||
localOptions.NoHeaders = true
|
localOptions.NoHeaders = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(table.ColumnDefinitions) == 0 {
|
if len(table.ColumnDefinitions) == 0 {
|
||||||
// If this table has no column definitions, use the columns from the last table we printed for decoration and layout.
|
// If this table has no column definitions, use the columns from the last table we printed for decoration and layout.
|
||||||
// This is done when receiving tables in watch events to save bandwidth.
|
// This is done when receiving tables in watch events to save bandwidth.
|
||||||
localOptions.NoHeaders = true
|
|
||||||
table.ColumnDefinitions = h.lastColumns
|
table.ColumnDefinitions = h.lastColumns
|
||||||
} else {
|
} else if !reflect.DeepEqual(table.ColumnDefinitions, h.lastColumns) {
|
||||||
// If this table has column definitions, remember them for future use.
|
// If this table has column definitions, remember them for future use.
|
||||||
h.lastColumns = table.ColumnDefinitions
|
h.lastColumns = table.ColumnDefinitions
|
||||||
|
h.printedHeaders = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(table.Rows) > 0 {
|
||||||
|
h.printedHeaders = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := decorateTable(table, localOptions); err != nil {
|
if err := decorateTable(table, localOptions); err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user