Skip to content

Commit e70a630

Browse files
committed
Added 'No resources found' message to describe and top pod commands
1 parent a71586f commit e70a630

File tree

4 files changed

+171
-0
lines changed

4 files changed

+171
-0
lines changed

staging/src/k8s.io/kubectl/pkg/cmd/describe/describe.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,15 @@ func (o *DescribeOptions) Run() error {
203203
}
204204
}
205205

206+
if len(infos) == 0 && len(allErrs) == 0 {
207+
// if we wrote no output, and had no errors, be sure we output something.
208+
if o.AllNamespaces {
209+
fmt.Fprintln(o.ErrOut, "No resources found")
210+
} else {
211+
fmt.Fprintf(o.ErrOut, "No resources found in %s namespace.\n", o.Namespace)
212+
}
213+
}
214+
206215
return utilerrors.NewAggregate(allErrs)
207216
}
208217

staging/src/k8s.io/kubectl/pkg/cmd/describe/describe_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,59 @@ func TestDescribeHelpMessage(t *testing.T) {
245245
}
246246
}
247247

248+
func TestDescribeNoResourcesFound(t *testing.T) {
249+
testNS := "testns"
250+
testCases := []struct {
251+
name string
252+
flags map[string]string
253+
namespace string
254+
expectedOutput string
255+
expectedErr string
256+
}{
257+
{
258+
name: "all namespaces",
259+
flags: map[string]string{"all-namespaces": "true"},
260+
expectedOutput: "",
261+
expectedErr: "No resources found\n",
262+
},
263+
{
264+
name: "all in namespace",
265+
namespace: testNS,
266+
expectedOutput: "",
267+
expectedErr: "No resources found in " + testNS + " namespace.\n",
268+
},
269+
}
270+
cmdtesting.InitTestErrorHandler(t)
271+
for _, testCase := range testCases {
272+
t.Run(testCase.name, func(t *testing.T) {
273+
pods, _, _ := cmdtesting.EmptyTestData()
274+
tf := cmdtesting.NewTestFactory().WithNamespace(testNS)
275+
defer tf.Cleanup()
276+
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
277+
278+
tf.UnstructuredClient = &fake.RESTClient{
279+
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
280+
Resp: &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, pods)},
281+
}
282+
283+
streams, _, buf, errbuf := genericclioptions.NewTestIOStreams()
284+
285+
cmd := NewCmdDescribe("kubectl", tf, streams)
286+
for name, value := range testCase.flags {
287+
_ = cmd.Flags().Set(name, value)
288+
}
289+
cmd.Run(cmd, []string{"pods"})
290+
291+
if e, a := testCase.expectedOutput, buf.String(); e != a {
292+
t.Errorf("Unexpected output:\nExpected:\n%v\nActual:\n%v", e, a)
293+
}
294+
if e, a := testCase.expectedErr, errbuf.String(); e != a {
295+
t.Errorf("Unexpected error:\nExpected:\n%v\nActual:\n%v", e, a)
296+
}
297+
})
298+
}
299+
}
300+
248301
type testDescriber struct {
249302
Name, Namespace string
250303
Settings describe.DescriberSettings

staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,13 @@ func (o TopPodOptions) RunTopPod() error {
196196
if e != nil {
197197
return e
198198
}
199+
200+
// if we had no errors, be sure we output something.
201+
if o.AllNamespaces {
202+
fmt.Fprintln(o.ErrOut, "No resources found")
203+
} else {
204+
fmt.Fprintf(o.ErrOut, "No resources found in %s namespace.\n", o.Namespace)
205+
}
199206
}
200207
if err != nil {
201208
return err

staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,108 @@ func TestTopPodWithMetricsServer(t *testing.T) {
449449
}
450450
}
451451

452+
func TestTopPodNoResourcesFound(t *testing.T) {
453+
testNS := "testns"
454+
testCases := []struct {
455+
name string
456+
options *TopPodOptions
457+
namespace string
458+
expectedOutput string
459+
expectedErr string
460+
expectedPath string
461+
}{
462+
{
463+
name: "all namespaces",
464+
options: &TopPodOptions{AllNamespaces: true},
465+
expectedOutput: "",
466+
expectedErr: "No resources found\n",
467+
expectedPath: topMetricsAPIPathPrefix + "/pods",
468+
},
469+
{
470+
name: "all in namespace",
471+
namespace: testNS,
472+
expectedOutput: "",
473+
expectedErr: "No resources found in " + testNS + " namespace.\n",
474+
expectedPath: topMetricsAPIPathPrefix + "/namespaces/" + testNS + "/pods",
475+
},
476+
}
477+
cmdtesting.InitTestErrorHandler(t)
478+
for _, testCase := range testCases {
479+
t.Run(testCase.name, func(t *testing.T) {
480+
fakemetricsClientset := &metricsfake.Clientset{}
481+
fakemetricsClientset.AddReactor("list", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) {
482+
res := &metricsv1beta1api.PodMetricsList{
483+
ListMeta: metav1.ListMeta{
484+
ResourceVersion: "2",
485+
},
486+
Items: nil, // No metrics found
487+
}
488+
return true, res, nil
489+
})
490+
491+
tf := cmdtesting.NewTestFactory().WithNamespace(testNS)
492+
defer tf.Cleanup()
493+
494+
ns := scheme.Codecs.WithoutConversion()
495+
496+
tf.Client = &fake.RESTClient{
497+
NegotiatedSerializer: ns,
498+
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
499+
switch p := req.URL.Path; {
500+
case p == "/api":
501+
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
502+
case p == "/apis":
503+
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apisbodyWithMetrics)))}, nil
504+
case p == "/api/v1/namespaces/" + testNS + "/pods":
505+
// Top Pod calls this endpoint to check if there are pods whenever it gets no metrics,
506+
// so we need to return no pods for this test scenario
507+
body, _ := marshallBody(metricsv1alpha1api.PodMetricsList{
508+
ListMeta: metav1.ListMeta{
509+
ResourceVersion: "2",
510+
},
511+
Items: nil, // No pods found
512+
})
513+
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: body}, nil
514+
default:
515+
t.Fatalf("%s: unexpected request: %#v\nGot URL: %#v",
516+
testCase.name, req, req.URL)
517+
return nil, nil
518+
}
519+
}),
520+
}
521+
tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
522+
streams, _, buf, errbuf := genericclioptions.NewTestIOStreams()
523+
524+
cmd := NewCmdTopPod(tf, nil, streams)
525+
var cmdOptions *TopPodOptions
526+
if testCase.options != nil {
527+
cmdOptions = testCase.options
528+
} else {
529+
cmdOptions = &TopPodOptions{}
530+
}
531+
cmdOptions.IOStreams = streams
532+
533+
if err := cmdOptions.Complete(tf, cmd, nil); err != nil {
534+
t.Fatal(err)
535+
}
536+
cmdOptions.MetricsClient = fakemetricsClientset
537+
if err := cmdOptions.Validate(); err != nil {
538+
t.Fatal(err)
539+
}
540+
if err := cmdOptions.RunTopPod(); err != nil {
541+
t.Fatal(err)
542+
}
543+
544+
if e, a := testCase.expectedOutput, buf.String(); e != a {
545+
t.Errorf("Unexpected output:\nExpected:\n%v\nActual:\n%v", e, a)
546+
}
547+
if e, a := testCase.expectedErr, errbuf.String(); e != a {
548+
t.Errorf("Unexpected error:\nExpected:\n%v\nActual:\n%v", e, a)
549+
}
550+
})
551+
}
552+
}
553+
452554
type fakeDiscovery struct{}
453555

454556
// ServerGroups returns the supported groups, with information like supported versions and the

0 commit comments

Comments
 (0)